zoukankan      html  css  js  c++  java
  • socket编程之黏包

    原理概述

      上图是我在学习python的socket编程中遇到的黏包问题所画,以实例来说明这个高大上的黏包问题。

      我们知道socket()实例中sendall()方法是无论数据有多大,一次性提交写入缓冲区(应用层);再来看接收端,recv()方法有个参数为buffsize,没错buffsize就是套接口的发送缓冲区的大小了。所以数据大于SO_SNDBUF的就会被分块传输,问题就来了,当两次提交的数据都比较大,刚好第一次尾与第二次的首同一时间待在了SO_SNDBUF里,被接收到了,这就是黏包。

      一句话:黏包最本质的原因就是接收方不知道接收的包有多大!

    解决方法(应用层维护消息和消息边界):

    1. 定长包
    2. 包尾加上 标记(FTP)
    3. 包头增加包体长度。
    4. 复杂的应用层协议。

    实例

      本实例多线程实例,实现的是客户端向服务端输入系统命令,服务器返回命令在本机上的执行结果。因为有些命令返回的结果是远大于1024的,所以可能出现黏包的问题。本实例的解决方案是第三条,server端每次向client端返回命令执行结果前,先发送包体大小并得到client端返回的确认信息,再发送数据,程序结构上避免了黏包问题。

    TCPSocket服务端

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    import SocketServer
    import os
    
    class Myserver(SocketServer.BaseRequestHandler):
    
        def handle(self):
            conn = self.request
            print "Client from:",self.client_address
            conn.sendall("请输入您要查询的命令")
            flag = True
            while flag:
                data = conn.recv(1024)
                print "receive cmd: %s"%data
                if data == "exit":
                    flag = False
                else:
                    ret = os.popen(data).read().decode("gbk").encode("utf-8")
             #发送包大小 conn.sendall(str(len(ret)))
    #收到客户端确认消息 scOK = conn.recv(1024)
             #发送包体内容 conn.sendall(ret)
    if __name__ == "__main__": server = SocketServer.ThreadingTCPServer(("localhost",8000),Myserver) server.serve_forever()

    TCPSocket客户端

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    import socket
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sk.connect(("127.0.0.1",8000))
    sk.settimeout(5)
    data = sk.recv(1024)
    print "SocketServer: %s" % data
    while True:
        reSize = 0
        msg = raw_input("Input:")
        sk.sendall(msg)
      #接收包的大小 totleSize
    = int(sk.recv(1024))
      #收到包的大小后,给server端发送确认信息。 sk.sendall(
    "It is ok") while True: data = sk.recv(1024) reSize += len(data)
        #当接收数据等于包的size后,跳出循环,停止 接收。
    if reSize == totleSize: print data break print data if msg == "exit": break sk.close()

     

  • 相关阅读:
    Encryption (hard) CodeForces
    cf 1163D Mysterious Code (字符串, dp)
    AC日记——大整数的因子 openjudge 1.6 13
    AC日记——计算2的N次方 openjudge 1.6 12
    Ac日记——大整数减法 openjudge 1.6 11
    AC日记——大整数加法 openjudge 1.6 10
    AC日记——组合数问题 落谷 P2822 noip2016day2T1
    AC日记——向量点积计算 openjudge 1.6 09
    AC日记——石头剪刀布 openjudge 1.6 08
    AC日记——有趣的跳跃 openjudge 1.6 07
  • 原文地址:https://www.cnblogs.com/chbo/p/6957872.html
Copyright © 2011-2022 走看看