在socket的server端处理client端发来的数据

一、楔子

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

  演示效果如下:

 

posted on 2019-04-15 16:59  江湖乄夜雨  阅读(353)  评论(0编辑  收藏  举报