socket 套接字的使用:
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
server 端
import socket sk = socket.socket() # 实例化一个socket的sk对象 sk.bind(('127.0.0.1', 10010)) # 设置IP和端口号 sk.listen() # 监听链接 conn, addr = sk.accept() # 接收客户端链接 conn.send('你好'.encode()) # conn.send(b'alex') 向客户端发送信息 # 如果服务器是发送,客户端要对应使用接收 ret = conn.recv(1024).decode() # 接收客户端信息 # 服务器是接收,客户端要对应发送 print(ret) # 打印客户端信息 conn.close() # 关闭客户端套接字 sk.close() # 关闭服务器套接字
client 端
import socket sk = socket.socket() #实例化一个socket的sk对象 sk.connect(('127.0.0.1', 10010)) # 设置ip和端口号,必须和服务器端相同 ret = sk.recv(1024).decode() #接收时要设置长度 print(ret) # 打印接收的信息 sk.send('我好'.encode()) # 发送信息给服务器端 sk.close()
UDP 链接:
server端
import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) # 对话(接收与发送) udp_sk.close() # 关闭服务器套接字
client端
import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr)
带退出功能的聊天
# server端: import socket sk = socket.socket() sk.bind(('127.0.0.1', 11111)) sk.listen() conn, addr = sk.accept() while True: msg = input('>>>: ') conn.send(msg.encode()) if msg == 'q': break ret = conn.recv(1024).decode() if ret == 'q':break print(ret) conn.close() sk.close() # client端 import socket sk = socket.socket() sk.connect(('127.0.0.1', 11111)) while True: ret = sk.recv(1024).decode() print(ret) if ret == 'q': break msg1 = input('>>>>: ') sk.send(msg1.encode()) if msg1 == 'q': break sk.close()
同步服务器时间:
# server 端: import socket import time sk = socket.socket() sk.bind(('127.0.0.1', 10010)) sk.listen() conn, addr = sk.accept() ret = conn.recv(1204).decode() str_time = time.strftime(ret) conn.send(str_time.encode()) conn.close() sk.close() # client端 import socket sk = socket.socket() sk.connect(('192.168.13.50', 10010)) time_type = '%Y-%m-%d' sk.send(time_type.encode('utf-8')) msg = sk.recv(1024) print(msg) sk.close()
黏包:
只有TCP有粘包现象,UDP永远不会粘包
# 发送一个 ‘hello’, 然后在发送一个‘world’ # 接收方接收到的是‘helloworld’ # 这就叫黏包
发送到的黏包
接收端的黏包
1.发送端的粘包 合包机制 + 缓存区
2.接收端的粘包 延迟接受 + 缓存区
总结
黏包现象只发生在tcp协议中:
1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
黏包的解决方法:
# 告诉客户端,你发送的数据长度
# server端 import struct import socket sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() conn,addr = sk.accept() send_msg = input('>>>').encode() bytes_len = struct.pack('i', len(send_msg)) conn.send(bytes_len) conn.send(send_msg) # 粘包现象 conn.send(b'world') conn.close() sk.close()
# client 端 import struct import socket sk = socket.socket() sk.connect(('127.0.0.1',9000)) bytes_len = sk.recv(4) msg_len = struct.unpack('i',bytes_len)[0] msg = sk.recv(msg_len) print(msg.decode()) msg2 = sk.recv(5) print(msg2) sk.close()
补充:
1.两个连续的send就会发生粘包
2.用struct自定义协议可以解决粘包问题
3.什么情况下我们不需要解决粘包 : 文件的传输
4.自定义协议的进阶版本
先发送字符串的长度,再发送字符串
先发送json的长度,再发送json,json的字典中包含着下一条信息的长度,然后按照长度接收