socket网络编程
简单架构:

套接字的工作流程:

套接字方法使用:
# ss = socket() # 创建服务器套接字 # ss.bind() # 套接字与地址绑定 # ss.listen() # 监听连接 # inf_loop: # 服务器无限循环 # cs = ss.accept() # 接受客户端连接 # comm_loop: # 通信循环 # cs.recv()/cs.send() # 对话(接收/发送) # cs.close() # 关闭客户端套接字 # ss.close() # 关闭服务器套接字(可选) import socket import sys from time import ctime # 1.socket(socket_family, socket_type, protocol=0) # 其中,socket_family 是 AF_UNIX 或 AF_INET,ocket_type 是 SOCK_STREAM或 SOCK_DGRAM, protocol 通常省略,默认为 0。 # 为了创建 TCP/IP 套接字,可以用下面的方式调用 socket.socket()。 # tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 同样,为了创建 UDP/IP 套接字,需要执行以下语句。 # udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 获取本地机器名 host = socket.gethostname() # 设置端口 port = 9999 # 2.s.bind绑定本地地址到socket对象 ServerSocket.bind((host, port)) # 3.s.listen监听地址端口,连接几个客户端 ServerSocket.listen(2) while True: # 4.s.accept阻塞接受链接请求,被动接受 TCP 客户端连接,一直等待直到连接到达(阻塞) # accept()方法会返回一个含有两个元素的元组(fd,addr)。 # 第一个元素是新的socket对象,服务器通过它与客户端通信。 # 第二个元素也是元组,是客户端的地址及端口信息。 clientsocket, addr = ServerSocket.accept() print("连接地址:%s" % str(addr)) msg = "welcomt to my demo" #send()和recv()的数据格式都是bytes。 # (str和bytes的相互转化,用encode()和decode(),或者用bytes()和str()) clientsocket.send(msg.encode("utf-8")) data = clientsocket.recv(1024) data1 = ('[%s] %s' % (ctime(),data.decode())).encode("utf-8") clientsocket.send(data1) clientsocket.close() ServerSocket.close()
socket对象:
| 参数 | 描述 |
| socket.AF_INET | IPv4(默认) |
| socket.AF_INET6 | IPv6 |
| socket.AF_UNIX | 只能够用于单一的Unix系统进程间通信 |
| 参数 | 描述 |
| socket.SOCK_STREAM | 流式socket , for TCP (默认) |
| socket.SOCK_DGRAM | 数据报式socket , for UDP |
| socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 |
| socket.SOCK_RDM | 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。 |
| socket.SOCK_SEQPACKET | 可靠的连续数据包服务 |
socket功能:

TCP
服务端 import socket sk = socket.socket() sk.bind(("127.0.0.1",8080)) sk.listen(5) while True: conn,address = sk.accept() conn.sendall(bytes("欢迎光临我爱我家",encoding="utf-8")) size = conn.recv(1024) size_str = str(size,encoding="utf-8") file_size = int(size_str) conn.sendall(bytes("开始传送", encoding="utf-8")) has_size = 0 f = open("db_new.jpg","wb") while True: if file_size == has_size: break date = conn.recv(1024) f.write(date) has_size += len(date) f.close() 客户端 import socket import os obj = socket.socket() obj.connect(("127.0.0.1",8080)) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") print(ret_str) size = os.stat("yan.jpg").st_size obj.sendall(bytes(str(size),encoding="utf-8")) obj.recv(1024) with open("yan.jpg","rb") as f: for line in f: obj.sendall(line)
UDP
import socket ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) sk.bind(ip_port) while True: data = sk.recv(1024) print data import socket ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) while True: inp = input('数据:').strip() if inp == 'exit': break sk.sendto(bytes(inp,encoding = "utf-8"),ip_port) sk.close()
socket粘包问题:
sk.recv(1024)中,bufsize值为1024,最多只能接受1024个字节,那么如果client端发送的数据包特别大时,超过了指定的bufsize的值,超过的不分会留在内核缓冲区中,下次调用recv的时候会继续读剩余的字节。这就是所谓的粘包问题,那么怎么解决呢?
类似于http协议,我们可以:
- 在发送之前先告诉接受数据端我要发送数据的字节大小
- 接收数据端收到数据后回复给数据发送端一个确认消息
- 数据发送端收到确认信息后,发送数据
- 数据接收端循环接受数据,直到数据接受完成,收到完整数据包

浙公网安备 33010602011771号