基于TCP,UDP协议的套接字通信以及SocketServer模块
网络编程
粘包现象
1.当发送数据大小大于接收数据时,在下次接收时会接收到上次内有接收完的数据
2.Nagle算法,将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包(TCP协议无消息保护边界)
基于TCP协议的套接字通讯
服务端
import socket
import subprocess
import struct
import json
ip_port = ('127.0.0.1',9000)
serve = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
serve.bind(ip_port)
serve.listen(5)
while True:
conn,addr = serve.accept()
while True:
try:
cmd = conn.recv(1024)
if not cmd:break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
data_size = len(stdout+stderr)
head_dic = {
'title':'asasd.txt',
'data_size':data_size
}
head_dic_str = json.dumps(head_dic)
head_dic_str_len = len(head_dic_str)
pack_head = struct.pack('i',head_dic_str_len)
conn.send(pack_head)
conn.send(head_dic_str.encode('utf-8'))
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
serve.close()
客户端
import socket
import struct
import json
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 9000))
while True:
cmd = input('>>>:')
if not cmd: continue
client.send(cmd.encode('utf-8'))
print('发送成功')
# 拿到被打包 报头的长度 (被打包成四位bytes字符串)
pack_head_dic_len = client.recv(4)
# 解包获得具体 字典的长度 元组(,)
head_dic_len = struct.unpack('i',pack_head_dic_len)
# 拿到字典的字节形式
head_dic_bys = client.recv(head_dic_len[0])
# 拿到字典的字符串形式
head_dic_str = head_dic_bys.decode('utf-8')
# 反序列化字符串形式的字典
head_dic = json.loads(head_dic_str)
# 取得传输文本的具体大小
data_size = head_dic['data_size']
# 循环接收数 并拼接bytes类型字符串
data_file = b''
current_size = 0
while current_size<data_size:
data = client.recv(1024)
current_size += len(data)
data_file = b"%s%s"%(data_file,data)
print(data_file.decode('gbk'))
基于UDP协议的套接字通讯
UDP协议
UDP叫数据报协议,意味着发消息都带有数据报头
udp的server不需要就行监听也不需要建立连接
在启动服务之后只能被动的等待客户端发送消息过来,客户端发送消息的时候,要带上服务端的地址
服务端在回复消息的时候,也需要带上客户端的地址
特点
- udp协议客户端允许发空
- udp协议不会粘包
- udp协议服务端不存在的情况下,客户端照样不会报错
- udp协议支持并发
服务端
import socket
serve = socket.socket(type=socket.SOCK_DGRAM)
serve.bind(('127.0.0.1',9009))
while True:
data,addr = serve.recvfrom(1024)
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' % (addr[0], addr[1], data.decode('utf-8')))
msg = input('回复消息:').strip().encode('utf-8')
serve.sendto(msg,addr)
服务端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
friend_dic = {
'aaa':('127.0.0.1',9009),
'bbb':('127.0.0.1',9009),
'ccc':('127.0.0.1',9009),
}
while True:
friend_name = input('请输入对方的名称:').strip()
while True:
msg = input('发送消息:').strip()
if msg=='q':break
client.sendto(msg.encode('utf-8'),friend_dic[friend_name])
rec_msg = client.recvfrom(1024)
print('%s回复一条消息:%s'%(friend_name,rec_msg[0].decode('utf-8')))
小知识:windows电脑和max电脑的时间同步功能,其实就是基于udp朝windows,max服务器发送请求获取标准时间
SocketServer模块
# TCP socketserver使用
import socketserver
class MyTcpServer(socketserver.BaseRequestHandler):
def handle(self):
while True:
try:
data = self.request.recv(1024) # 对于tcp,self.request相当于conn对象
if len(data) == 0:break
print(data)
self.request.send(data.upper())
except ConnectionResetError:
break
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyTcpServer)
server.serve_forever()
# UDP socketserver使用
import socketserver
class MyUdpServer(socketserver.BaseRequestHandler):
def handle(self):
while True:
data, sock = self.request
print(data)
sock.sendto(data.upper(), self.client_address)
if __name__ == '__main__':
server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyUdpServer)
server.serve_forever()

浙公网安备 33010602011771号