1,socket通信
2,socket对象的参数
socket families:网络层
socket.AF_INET# IPV4
socket.AF_INET6# IPV6
socket.AF_UNIX # unix本机进程间通信
socket types:传输层
socket.SOCK_STREAM #TCP
socket.SOCK_DGRAM #UDP
socket.SOCK_RAW #原始套接字,可以伪造IP头
socket.SOCK_RDM #UDP,保证传到,但不保证顺序
3,黏包
场景:
单次接收设置1024,但是实际数据大于1024,多于1024的内容会再下次接收到
服务器连续两次send,可能会被放进缓冲区形成黏包
解决方法:
计算发送内容大小,先把大小发送给接收方,发送完大小后服务器必须加阻塞确认,防止黏包
拿到内容大小后,后续如果还有多次send多问题,就可以改用精确接收内容大小防止黏包了,无需每次阻塞确认
特别注意中文str长度1,变成bytes后长度是3
4,模拟SSH
服务器端:
import socket import os server = socket.socket() server.bind(('localhost', 19999)) # 绑定端口 server.listen(3) # 监听端口,这里不是并发 while True: conn, addr = server.accept() # 等待连接,进入阻塞状态 while True: cmd = conn.recv(1024) # 接收大小是1024 if not cmd: # linux会陷入recv死循环,建议都加上防止反复接收死循环 break cmd = cmd.decode() cmd_res = os.popen(cmd).read() cmd_lens = len(cmd_res) conn.send(str(cmd_lens).encode()) client_ack = conn.recv(1024) # 加个确认防止黏包 conn.send(cmd_res.encode()) # server.close() # 这里如果加close,server不会立即关闭,会在当前client结束后再关
客户端:
import socket client = socket.socket() client.connect(('localhost', 19999)) while True: msg = input('>>: ').strip() if not msg: continue elif msg == 'exit': # 退出循环,关闭连接 break client.send(msg.encode()) # 只能send bytes,默认utf-8 cmd_res_size = client.recv(1024) cmd_res_size = int(cmd_res_size.decode()) # bytes -> str -> int client.send(b'ack') # 发送确认,解决长度与实际data之间的黏包问题 received_size = 0 received_data = b'' while received_size < cmd_res_size: data = client.recv(1024) received_size += len(data) received_data += data print(received_data.decode()) client.close()