在socket的server端处理client端发来的数据
2024-10-03 01:58:47
一、楔子
最近做了一个需求遇到一个坑,归结成一个小问题,其实就是在socket的server端处理client端发来的数据的问题,现将这个问题总结一下,本文将数据在server端以字典的形式存储。
另外,由于我想做一个动图的演示,所以本文没有用MarkDown写,希望本文的干货能弥补格式带来的不适ε=(´ο`*)))
二、基于原生UDP的实现
我们这里用“默认字典”去实现数据的构建,基于原生UDP的代码如下:
# -*- coding:utf-8 -*-
import socket
from collections import defaultdict server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',9002))
print('Listening......') client_msg = {
'':'Naruto',
'':'Sasuke',
} dic_msg = defaultdict(list) while 1:
try:
# 接收
recv_msg,addr = server.recvfrom(1024)
recv_msg = recv_msg.decode('utf-8')
# 这里只切割一次,避免后面的数据还有|符号
cid = recv_msg.strip().split('|',1)[0]
msg = recv_msg.strip().split('|',1)[1] # 默认字典
dic_msg[client_msg[cid]]
dic_msg[client_msg[cid]].append(msg) # 回复
server.sendto('收到了'.encode('utf-8'),addr)
print('消息字典:%s'%(dict(dic_msg)))
except Exception as e:
print(e)
Server端
# -*- coding:utf-8 -*-
import socket client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1',9002)
cid = ''
while 1:
try:
msg = input('>>>:').strip()
# 发送 cid跟消息用|分割开来——具体怎么做看具体需求
send_msg= cid+'|'+msg
client.sendto(send_msg.encode('utf-8'),server_addr)
# 接收
content = client.recv(1024).decode('utf-8')
print('server端回复:',content)
except Exception as e:
print(e)
break
Client端
实现效果如下:
三、基于并发的TCP实现
socketsever模块为我们提供了并发的功能,我们这里直接用socketsever模块做。
另外我们考虑到TCP的粘包问题,以Client端给Server端发送数据为例:我们需要先给Server端发送一个带有”信息“的字典。这里的信息可以是我们将要发送的数据的大小,也可以是其他的必要的数据,这里我们给Server端发送Client端的标识cid以及将要发送的数据的大小。
同时考虑到程序的解耦,我们把包含重要信息字典的处理放在pro_trance.py文件中,两端在收发信息的时候直接import即可。里面的代码为:
# -*- coding:utf-8 -*-
import json
import struct def pro_send(sk,dic,pro=True):
str_dic = json.dumps(dic)
bytes_dic = str_dic.encode('utf-8')
# 默认选择协议发送
if pro:
num_dic = struct.pack('i',len(bytes_dic))
sk.sendall(num_dic)
sk.sendall(bytes_dic) def pro_recv(sk,pro=True):
# 依据协议先收到的是字典的长度
if pro:
num_dic = sk.recv(4)
# 注意unpack得到的是一个元组,需要加上0索引取值
num = struct.unpack('i',num_dic)[0]
str_dic = sk.recv(num).decode('utf-8')
dic = json.loads(str_dic)
# 不根据协议接收直接用1024大小的长度来接收
else:
dic = json.loads(sk.recv(1024).decode('utf-8'))
return dic
pro_trance.py
接下来是Client端与Sever端的代码,这里模拟不同标识的客户端能够同时访问Server端的情况:
# -*- coding:utf-8 -*-
import socket from pro_trance import pro_send,pro_recv cid = ''
client = socket.socket()
client.connect(('127.0.0.1',9003))
while 1:
# 这里用msg模拟长数据,所以选择避免粘包的发送方式
msg = input('>>>:').strip()
bytes_msg = msg.encode('utf-8')
len_by_msg = len(bytes_msg)
dic = {'cid':cid,'data_size':len_by_msg}
# 协议发送携带信息的字典
pro_send(client,dic)
# 再发送数据
client.sendall(bytes_msg)
# 接收 —— 直接收 实际是否考虑粘包视具体设计而定
str_recv = client.recv(1024).decode('utf-8')
print('server reply:',str_recv)
Client1端
# -*- coding:utf-8 -*-
import socket from pro_trance import pro_send,pro_recv cid = ''
client = socket.socket()
client.connect(('127.0.0.1',9003))
while 1:
# 这里用msg模拟长数据,所以选择避免粘包的发送方式
msg = input('>>>:').strip()
bytes_msg = msg.encode('utf-8')
len_by_msg = len(bytes_msg)
dic = {'cid':cid,'data_size':len_by_msg}
# 协议发送携带信息的字典
pro_send(client,dic)
# 再发送数据
client.sendall(bytes_msg)
# 接收 —— 直接收 实际是否考虑粘包视具体设计而定
str_recv = client.recv(1024).decode('utf-8')
print('server reply:',str_recv)
Client2端
# -*- coding:utf-8 -*-
import socketserver
from collections import defaultdict from pro_trance import pro_send,pro_recv # 一般这种数据都是放在settings文件中的
client_msg = {
'':'Naruto',
'':'Sasuke',
} class Server(socketserver.BaseRequestHandler):
def handle(self):
dic_msg = defaultdict(list)
while 1:
# 根据协议接收信息字典
dic = pro_recv(self.request)
# client端的id
cid = dic['cid']
data_size = dic['data_size']
# 根据数据的长度接收
msg = self.request.recv(data_size).decode('utf-8')
# 利用之前的默认字典构建数据
dic_msg[client_msg[cid]]
dic_msg[client_msg[cid]].append(msg)
print('client %s,message:%s' % (client_msg[cid], dict(dic_msg)))
# 回复
self.request.sendall('received successfully!'.encode('utf-8'))
## 注意socketserver这里不用加close方法
## BaseRequestHandler有一个finish的相关的方法 if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',9003),Server)
server.serve_forever()
Server端
演示效果如下:
最新文章
- Tomcat类加载器机制
- MongoDB 查询 (转) 仅限于C++开发
- sqlMetal用法和例子 自定义DBML
- List转xml
- The package does not support the device architecture (x86). You can change the supported architectures in the Android Build section of the Project Opt
- iOS中Block介绍 基础
- PAT 1006. Sign In and Sign Out
- iOS开发之Runtime常用示例总结
- Qt日常备注(函数/接口实现)
- java课设-计算数学表达式的程序,201521123050,肖世松,个人
- nginx系列13:最少连接算法以及如何跨worker进程生效
- SQL学习指南第二篇
- Python开发【第十篇】:Redis
- postman+linux+pistache的http通信过程
- oracle导出大数据
- hdu 1106 去5排序
- webpack 搭建问题汇总
- 大数据入门第二十四天——SparkStreaming(一)入门与示例
- 解决SSH登录用户执行的命令部分环境变量参数不生效的问题
- 区间内x的出现个数(主席树)
热门文章
- 博客 新址: https://pheromone.github.io/
- python基础---列表生成器、迭代器等
- unicode,gbk,utfF-8字符编码方式的区别
- 增删改查js
- javascript 5.2
- 谈一谈java中的Canves机制
- linux下禁用网卡的启用网卡的一些方法
- 使用ghost装完系统后出现“引用了一个不可用的位置”
- java web(四):request、response一些用法和文件的上传和下载
- 类似py2exe软件真的能保护python源码吗