一、楔子

  最近做了一个需求遇到一个坑,归结成一个小问题,其实就是在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端

  演示效果如下:

最新文章

  1. Tomcat类加载器机制
  2. MongoDB 查询 (转) 仅限于C++开发
  3. sqlMetal用法和例子 自定义DBML
  4. List转xml
  5. The package does not support the device architecture (x86). You can change the supported architectures in the Android Build section of the Project Opt
  6. iOS中Block介绍 基础
  7. PAT 1006. Sign In and Sign Out
  8. iOS开发之Runtime常用示例总结
  9. Qt日常备注(函数/接口实现)
  10. java课设-计算数学表达式的程序,201521123050,肖世松,个人
  11. nginx系列13:最少连接算法以及如何跨worker进程生效
  12. SQL学习指南第二篇
  13. Python开发【第十篇】:Redis
  14. postman+linux+pistache的http通信过程
  15. oracle导出大数据
  16. hdu 1106 去5排序
  17. webpack 搭建问题汇总
  18. 大数据入门第二十四天——SparkStreaming(一)入门与示例
  19. 解决SSH登录用户执行的命令部分环境变量参数不生效的问题
  20. 区间内x的出现个数(主席树)

热门文章

  1. 博客 新址: https://pheromone.github.io/
  2. python基础---列表生成器、迭代器等
  3. unicode,gbk,utfF-8字符编码方式的区别
  4. 增删改查js
  5. javascript 5.2
  6. 谈一谈java中的Canves机制
  7. linux下禁用网卡的启用网卡的一些方法
  8. 使用ghost装完系统后出现“引用了一个不可用的位置”
  9. java web(四):request、response一些用法和文件的上传和下载
  10. 类似py2exe软件真的能保护python源码吗