运维开发网
广告位招商联系QQ:123077622
 
广告位招商联系QQ:123077622

socket,与粘包

运维开发网 https://www.qedev.com 2020-07-13 12:51 出处:网络 作者:运维开发网整理
1,socket(套接字)     socket层                    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口       server端         import socket         sk = socket.socket()         sk.bind((‘127.0.0.1‘,8898))  #把地址绑定到套接字

  1,socket(套接字)

    socket层

 

        

socket,与粘包

        Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口

      server端

        import socket

        sk = socket.socket()

        sk.bind((‘127.0.0.1‘,8898))  #把地址绑定到套接字

        sk.listen()          #监听链接

        conn,addr = sk.accept() #接受客户端链接

        ret = conn.recv(1024)  #接收客户端信息

        print(ret)       #打印客户端信息

        conn.send(b‘hi‘)        #向客户端发送信息

        conn.close()       #关闭客户端套接字

        sk.close()        #关闭服务器套接字           根据情况选择

      client 端

        import socket

        sk = socket.socket()           # 创建客户套接字

        sk.connect((‘127.0.0.1‘,8898))    # 尝试连接服务器

        sk.send(b‘hello!‘)

        ret = sk.recv(1024)         # 对话(发送/接收)

        print(ret)

        sk.close()            # 关闭客户套接字

       2,粘包

    1,什么是粘包

例如基于tcp的套接字客户端往服务端上传文件,

发送时文件内容是按照一段一段的字节流发送的,

在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束

2,粘包是怎么造成的

发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,

发送方往往要收集到足够多的数据后才发送一个TCP段。

若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,

这样接收方就收到了粘包数据。

3,tcp传输的原理

用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓冲区的大小),

这是指在用send函数时,数据长度参数不受限制。

而实际上,所指定的这段数据并不一定会一次性发送出去,

      TCP特点:

         会将数据量比较小的并且时间间隔比较短的数据

         一次性打包发送给对方

4,发生黏包的两种情况:

情况一 发送方的缓存机制

发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

情况二 接收方的缓存机制

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,

服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

ps:主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的 *****************

    

  解决方案:

struct 模块

服务端 1.先制作一个发送给客户端的字典

2.制作字典的报头

3.发送字典的报头

4.发送字典

5.再发真实数据

客户端

1.先接受字典的报头

2.解析拿到字典的数据长度

3.接受字典

4.从字典中获取真实数据的长度

5.接受真实数据

    把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节

      

发送时 接收时

先发报头长度

先收报头长度,用struct取出来
再编码报头内容然后发送 根据取出的长度收取报头内容,然后解码,反序列化
最后发真实内容 从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

    客户端  

                  import socket                  import struct                  import json                  print("客户端")                                    client = socket.socket()                                    # 绑定服务器ip和端口                  client.connect((‘127.0.0.1‘, 8333))                  # 循环收发数据                  while True:                      msg = input(‘>>>>>:‘).encode(‘utf-8‘)                      # 为了防止出现你等我,我等你这种情况,(客户端按了end,服务端没接受到数据                      #  而客户端此时也走到了recv阶段,服务端一直处于recv阶段,所以一直处于阻塞状态                      if len(msg) == 0:continue   # 所以要有这个判断,continue结束本次循环                      client.send(msg)                      # 先接受报头长度                      header_dict = client.recv(4)                      # 解析报头,获取字典的长度                      dict_size = struct.unpack(‘i‘, header_dict)[0]                      dict_bytes = client.recv(dict_size)                      dict_json = json.loads(dict_bytes.decode(‘utf-8‘))                      print(dict_json)                      recv_size = 0                      real_data = b‘‘                      while recv_size < dict_json.get(‘file_size‘):                          data = client.recv(1024)                          real_data += data                          recv_size += len(data)                      print(real_data.decode(‘gbk‘))                      client.close()       服务端:                                          import socket                  import subprocess                  import struct                  import json                                    server = socket.socket()                  server.bind((‘127.0.0.1‘,8080))                  server.listen(5)                                    while True:                      conn, addr = server.accept()                      while True:                          try:                              cmd = conn.recv(1024)                              if len(cmd) == 0:break                              cmd = cmd.decode(‘utf-8‘)                              obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)                              res = obj.stdout.read() + obj.stderr.read()                              d = {‘name‘:‘jason‘,‘file_size‘:len(res),‘info‘:‘asdhjkshasdad‘}                              json_d = json.dumps(d)                              # 1.先制作一个字典的报头                              header = struct.pack(‘i‘,len(json_d))                              # 2.发送字典报头                              conn.send(header)                              # 3.发送字典                              conn.send(json_d.encode(‘utf-8‘))                              # 4.再发真实数据                              conn.send(res)                              # conn.send(obj.stdout.read())                              # conn.send(obj.stderr.read())                          except ConnectionResetError:                              break                      conn.close()

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消