一、楔子

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

  演示效果如下:

在socket的server端处理client端发来的数据的更多相关文章

  1. 用同一台PC的两个网口实现Iperf的server端和client端

    用同一台PC的两个网口实现Iperf的server端和client端 2015年10月20日 20:35:11 阅读数:2943 有时候需要发包,仅仅需要一定速率的流量,并不需要关心收到报文的大小,一 ...

  2. 【Tech】CAS多机部署Server和Java Client端

    昨天尝试把cas的java client端部署到另外一台机器,结果就有问题了.(localhost部署cas server和java client端参见:http://www.cnblogs.com/ ...

  3. 微服务学习三:springboot与springcloud集成之Eurake的使用(server端,client端)

    这个多亏了网站上的一个大神的博客: http://blog.csdn.net/forezp/article/details/70148833 强烈推荐学习: 1.springcloud是什么,这个大家 ...

  4. 使用gRPC搭建Server端与Client端

    gRPC简介 gRPC是一种RPC框架技术,采用Protocal Buffers(协议缓存) 作为其接口定义的语言(就是Proto来写接口)和基础的消息交换格式. 在gRPC中,客户端应用程序可以直接 ...

  5. Spring Cloud config之三:config-server因为server端和client端的健康检查导致服务超时阻塞问题

    springcloud线上一个问题,当config-server连不上git时,微服务集群慢慢的都挂掉. 在入口层增加了日志跟踪问题: org.springframework.cloud.config ...

  6. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  7. Java Socket 连接 Client端 和 Server端

    Client端: import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;i ...

  8. Linux下的C Socket编程 -- server端的简单示例

    Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...

  9. Linux下的C Socket编程 -- 简介与client端的处理

    Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...

随机推荐

  1. type() 和 isinstance()区别

    a=111 # type() 返回数据类型 In: type(a)  Out: int In: print(type(a)) Out: <class 'int'> # isinstance ...

  2. 2018-计算机系机试(第二批)-E-绝对值排序

    单点时限: 2.0 sec 内存限制: 256 MB 输入 n 个整数,按照绝对值从大到小排序.绝对值相等的整数按照整数值从小到大排序. 例如:3 个整数 -2,2 和 -6 的排序结果为 -6, - ...

  3. 微信小程序页面跳转,带参数跳转

    1.  wx.navigateTo  (保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面.) 路径:只能跳转非tabbar路径 参数:  'path?key=v ...

  4. ES6学习重难点总结(持续更新)

    Symbol 1.Symbol.iterator Symbol.iterator指向对象的默认遍历器方法: String.prototype[Symbol.iterator] //ƒ [Symbol. ...

  5. Centos7.5系统 SSH升级到7.9

    SSH7.9安装 #!/bin/bash#删除旧版ssh包 危险操作,不删除也可以安装,建议跳过此操作.#rpm -e `rpm -qa | grep openssh` #安装zlib依赖包wget ...

  6. 动态改变Spring定时任务执行频率

    @Component@EnableSchedulingpublic class updateCronTask implements SchedulingConfigurer { public stat ...

  7. js数组操作-最佳图解

    js数组操作-最佳图解

  8. spring boot 项目部署在阿里云上

    装jdk, 然后 nohup java -jar xxxx.jar> system.log 2>&1 & lsof -i:9999 kill -9 1234

  9. Unity备份占时留用

    Unity开发VR之Vuforia 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...

  10. jmeter 获取登录token

    1.在登录的请求下新建正则表达式,获取token 2.正则表达式的写法 注意:apply to 默认是选择第二个,但是有获取不到token的情况就选第一个 "accessToken" ...