zoukankan      html  css  js  c++  java
  • python开发socket套接字:粘包问题&udp套接字&socketserver

    一,发生粘包

    服务器端

     1 from socket import *
     2 phone=socket(AF_INET,SOCK_STREAM)        #套接字
     3 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)   #解决端口占用
     4 phone.bind(('127.0.0.1',8080))   #绑定端口和Ip到套接字
     5 phone.listen(5)
     6 conn,client_addr=phone.accept()   #等待tcp接受
     7 
     8 
     9 # data1=conn.recv(10)
    10 # print('data1: ',data1)
    11 # data2=conn.recv(4)
    12 # print('data2:',data2)
    13 #接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,
    14 #服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

    客户端

    1 from socket import *
    2 import time
    3 phone=socket(AF_INET,SOCK_STREAM)
    4 phone.connect(('127.0.0.1',8080))
    5 
    6 
    7 # phone.send('helloworld'.encode('utf-8'))
    8 # phone.send('egon'.encode('utf-8'))
    9 #发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

    二,用struct模块解决粘包问题

    为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

     1 #_*_coding:utf-8_*_
     2 #http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
     3 __author__ = 'Linhaifeng'
     4 import struct
     5 import binascii
     6 import ctypes
     7 
     8 values1 = (1, 'abc'.encode('utf-8'), 2.7)
     9 values2 = ('defg'.encode('utf-8'),101)
    10 s1 = struct.Struct('I3sf')
    11 s2 = struct.Struct('4sI')
    12 
    13 print(s1.size,s2.size)
    14 prebuffer=ctypes.create_string_buffer(s1.size+s2.size)
    15 print('Before : ',binascii.hexlify(prebuffer))
    16 # t=binascii.hexlify('asdfaf'.encode('utf-8'))
    17 # print(t)
    18 
    19 
    20 s1.pack_into(prebuffer,0,*values1)
    21 s2.pack_into(prebuffer,s1.size,*values2)
    22 
    23 print('After pack',binascii.hexlify(prebuffer))
    24 print(s1.unpack_from(prebuffer,0))
    25 print(s2.unpack_from(prebuffer,s1.size))
    26 
    27 s3=struct.Struct('ii')
    28 s3.pack_into(prebuffer,0,123,123)
    29 print('After pack',binascii.hexlify(prebuffer))
    30 print(s3.unpack_from(prebuffer,0))
    31 
    32 关于struct的详细用法

    服务器端

     1 import socket
     2 import subprocess
     3 import struct
     4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
     6 phone.bind(('127.0.0.1',8088)) #绑定手机卡
     7 phone.listen(5) #开机
     8 
     9 print('starting...')
    10 while True: #链接循环
    11     conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    12     print('-------->',conn,client_addr)
    13 
    14     #收,发消息
    15     while True:#通信循环
    16         try:
    17             cmd=conn.recv(1024)
    18             print(cmd)
    19             if not cmd:break #针对linux
    20             #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
    21             #。。。。
    22             res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
    23                                    stdout=subprocess.PIPE,
    24                                    stderr=subprocess.PIPE)
    25             stdout=res.stdout.read()
    26             stderr=res.stderr.read()
    27             print(stdout)
    28 
    29             #先发报头(转成固定长度的bytes类型)
    30             header = struct.pack('i',len(stdout)+len(stderr))
    31             print(header)
    32             conn.send(header)
    33             #再发送命令的结果
    34             conn.send(stdout)
    35             conn.send(stderr)
    36         except Exception:
    37             break
    38     conn.close() #挂电话
    39 phone.close() #关机

    客户端

     1 import socket
     2 import struct
     3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     4 phone.connect(('127.0.0.1',8088)) #绑定手机卡
     5 
     6 #发,收消息
     7 while True:
     8     cmd=input('>>: ').strip()
     9     if not cmd:continue
    10 
    11     phone.send(cmd.encode('utf-8'))
    12     #先收报头
    13     header_struct=phone.recv(4)
    14     print(header_struct)
    15     unpack_res = struct.unpack('i', header_struct)
    16     total_size=unpack_res[0]
    17     print(total_size)
    18 
    19     #再收数据
    20     recv_size=0 #10241=10240+1
    21     total_data=b''
    22     while recv_size < total_size:
    23         recv_data=phone.recv(1024)
    24         print(recv_data)
    25         recv_size+=len(recv_data)
    26         print(recv_size)
    27         total_data+=recv_data
    28         print(total_data)
    29     # else:
    30     print(total_data.decode('gbk'))
    31 phone.close()

    三,大文件粘包问题

    服务器端

     1 import socket
     2 import subprocess
     3 import struct
     4 import json
     5 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     6 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
     7 phone.bind(('127.0.0.1',8082)) #绑定手机卡
     8 phone.listen(5) #开机
     9 
    10 print('starting...')
    11 while True: #链接循环
    12     conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    13     print('-------->',conn,client_addr)
    14 
    15     #收,发消息
    16     while True:#通信循环
    17         try:
    18             cmd=conn.recv(1024)
    19             if not cmd:break #针对linux
    20             #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
    21             #。。。。
    22             res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
    23                                    stdout=subprocess.PIPE,
    24                                    stderr=subprocess.PIPE)
    25             stdout=res.stdout.read()
    26             stderr=res.stderr.read()
    27             #制作报头
    28             header_dic = {
    29                 'total_size': len(stdout)+len(stderr),
    30                 'filename': None,
    31                 'md5': None}
    32 
    33             header_json = json.dumps(header_dic)
    34             header_bytes = header_json.encode('utf-8')
    35             #发送阶段
    36             #先发报头长度
    37             conn.send(struct.pack('i',len(header_bytes)))
    38             #再发报头
    39             conn.send(header_bytes)
    40 
    41             #最后发送命令的结果
    42             conn.send(stdout)
    43             conn.send(stderr)
    44         except Exception:
    45             break
    46     conn.close() #挂电话
    47 phone.close() #关机

    客户端

     1 import socket
     2 import struct
     3 import json
     4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     5 phone.connect(('127.0.0.1',8082)) #绑定手机卡
     6 
     7 #发,收消息
     8 while True:
     9     cmd=input('>>: ').strip()
    10     if not cmd:continue
    11 
    12     phone.send(cmd.encode('utf-8'))
    13     #先收报头的长度
    14     header_len=struct.unpack('i',phone.recv(4))[0]
    15 
    16     #再收报头
    17     header_bytes=phone.recv(header_len)
    18     header_json=header_bytes.decode('utf-8')
    19     header_dic=json.loads(header_json)
    20     total_size=header_dic['total_size']
    21 
    22     #最后收数据
    23     recv_size=0 #10241=10240+1
    24     total_data=b''
    25     while recv_size < total_size:
    26         recv_data=phone.recv(1024)
    27         recv_size+=len(recv_data)
    28         total_data+=recv_data
    29     print(total_data.decode('gbk'))
    30 phone.close()

    四,udp套接字

    服务器端

     1 from socket import *
     2 udp_server=socket(AF_INET,SOCK_DGRAM)
     3 udp_server.bind(('127.0.0.1',8088))
     4 
     5 while True:
     6     msg,client_addr=udp_server.recvfrom(1024)
     7     print('has recv %s' %msg)
     8     udp_server.sendto(msg.upper(),client_addr)
     9     print('has send')
    10 udp_server.close()

    客户端

     1 from socket import *
     2 udp_client=socket(AF_INET,SOCK_DGRAM)
     3 
     4 while True:
     5     msg=input('>>: ').strip()
     6     udp_client.sendto(msg.encode('utf-8'),('127.0.0.1',8088))
     7     print('has send')
     8     # res,server_addr=udp_client.recvfrom(1024)
     9     # print('====>',res.decode('utf-8'))
    10 
    11 udp_client.close()

    udp 套接字不会发生粘包

    服务器端

    1 from socket import *
    2 udp_server=socket(AF_INET,SOCK_DGRAM)
    3 udp_server.bind(('127.0.0.1',8089))
    4 
    5 msg1,client_addr=udp_server.recvfrom(5)
    6 print(msg1)
    7 
    8 msg2,client_addr=udp_server.recvfrom(5)
    9 print(msg2)

    客户端

    1 from socket import *
    2 udp_client=socket(AF_INET,SOCK_DGRAM)
    3 
    4 udp_client.sendto('hello'.encode('utf-8'),('127.0.0.1',8089))
    5 udp_client.sendto('world'.encode('utf-8'),('127.0.0.1',8089))

    五,socketserver套接字

    封装了socket,而且解决了Io阻塞问题

    服务端

     1 # socketserver模块多进程,多线程
     2 # 无论你开什么都是开线程,线程就有IO,这个模块帮你解决这个IO问题
     3 
     4 # 基础版本,遇到问题,不能无限开线程,而且没有解决IO
     5 # 线程开启跟进程开启一样
     6 from socket import *
     7 from threading import Thread
     8 # s=socket(AF_INET,SOCK_STREAM)
     9 # s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
    10 # s.bind(('127.0.0.1',8090))
    11 # s.listen(5)
    12 # def talk(conn,addr):
    13 #     while True: #通信循环
    14 #         try:
    15 #             data=conn.recv(1024)
    16 #             if not data:break
    17 #             conn.send(data.upper())
    18 #         except Exception:
    19 #             break
    20 #     conn.close()
    21 # if __name__ == '__main__':
    22 #     while True:#链接循环
    23 #         conn,addr=s.accept()
    24 #         p = Thread(target=talk,args=(conn,addr))
    25 #         p.start()
    26 #     s.close()
    27 
    28 # socketserver模块套接字,自动处理IO问题
    29 import socketserver
    30 class MyTCPhandler(socketserver.BaseRequestHandler):  #必须继承这个类
    31     def handle(self):
    32         # print(self.request)  打印出来的就是conn
    33         # print(self.client_address)   打印出来的就是addr
    34         while True:
    35             try:
    36                 data=self.request.recv(1024)
    37                 if not data:break
    38                 self.request.send(data.upper())
    39             except Exception:
    40                 break
    41         self.request.close()
    42 if __name__ == '__main__':
    43     server=socketserver.ThreadingTCPServer(('127.0.0.1',8082),MyTCPhandler)  #多线程
    44     # 三个参数,IP,端口,类
    45     # server=socketserver.ForkingTCPServer(('127.0.0.1',8082),MyTCPhandler)  #多进程
    46     server.allow_reuse_address=True   #重用地址
    47     server.serve_forever()   #永远运行,就是一直开着,相当于while True

    客户端

     1 from socket import *
     2 client=socket(AF_INET,SOCK_STREAM)
     3 client.connect(('127.0.0.1',8082))
     4 
     5 while True:
     6     msg=input('>>: ').strip()
     7     if not msg:continue
     8     client.send(msg.encode('utf-8'))
     9     data=client.recv(1024)
    10     print(data.decode("utf-8"))
  • 相关阅读:
    python requests用法总结
    Linux统计某文件夹下文件的个数
    PM2使用及介绍
    如何区分USB 2.0 和USB 3.0插口
    npm突然找不到npm-cli.js的解决方法
    mRemoteNG
    js中几种实用的跨域方法原理详解
    Tornado异步阻塞解决方案
    [阅读笔记]EfficientDet
    iOS 保存视频AVAssetWriter
  • 原文地址:https://www.cnblogs.com/jokerbj/p/7422349.html
Copyright © 2011-2022 走看看