zoukankan      html  css  js  c++  java
  • 网络编程


    1.粘包:

    多个包 多个命令的结果 粘到一起了 因为recv 1024限制了 导致的结果
    参考:http://www.cnblogs.com/linhaifeng/articles/6129246.html

    粘包底层原理分析:
    1.运行一个软件 和 哪几个硬件 有关
    硬盘 内存 cpu
    2.启动程序:
    硬盘程序 加载到 内存 启一个软件就占一个内存空间
    os 就有一个内存空间
    os的内存空间 和 软件的内存空间 彼此互相隔离

    须知:只有TCP有粘包现象,UDP永远不会粘包。
    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。


    3.send recv 底层原理
    send 应用程序的代码 把自己的数据 发出去 存放到自己的内存空间里
    发到os的内存里调网卡发数据
    程序的内存和os的内存两个内存互相隔离
    数据copy给os的内存
    send 发给了 自己的os的内存 os会照着tcp协议去发
    recv 通知os 去调网卡 收数据
    1.send发到数据到服务端os的内存 # 慢
    2.os的内存 copy 给程序 # 快
    站在应用程序角度上:
    send: 1.数据发给本地的os # 耗时短一些
    recv:1.通知os收数据 2.os内存数据copy到应用程序内存中 # 耗时长一些

    4.send recv 总结:
    1:不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv # 一发可以对应多收 一收对应多发
    2.:recv:
    wait data 耗时非常长
    copy data
    send:
    copy data
    3.数据量比较小 时间间隔比较短 就合并成一个包 再发
    使用了优化方法(Nagle算法)

    5.recv 有关部门建议的不要超过 8192,再大反而会出现影响收发速度和不稳定的情况

    服务端
     1 '''
     2 解决粘包的办法:
     3     明确知道对方给我发的包的长度
     4 '''
     5 import time
     6 import socket
     7 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     8 server.bind(('127.0.0.1',8080))
     9 server.listen(5)
    10 print('strating...')
    11 
    12 conn,addr=server.accept()
    13 
    14 # 第一次接收5
    15 # res1 = conn.recv(2)
    16 # res2 = conn.recv(2)
    17 res1 = conn.recv(5)
    18 print('第一次:',res1)
    19 time.sleep(6)
    20 #第二次接收
    21 res3 = conn.recv(6).decode('utf-8')
    22 print('第二次:',res3)
    23 
    24 '''
    25 第一次: b'helloworld'
    26 第二次: b''
    27 '''
    28 
    29 '''
    30 第一次: b'hello'
    31 第二次: b'world'
    32 '''
    33 
    34 '''
    35 第一次: b'h'
    36 第二次: b'ello' 
    37 '''
    38 
    39 '''
    40 第一次: b'h'
    41 第二次: b'elloworld'  # 客户端粘了 服务端粘了
    42 '''

    客户端
     1 '''
     2 粘包不一定会发生:
     3     数据量比较小 时间比较短 才会发生粘包
     4 
     5 粘包是tcp协议底层优化算法决定的!(Nagle算法)
     6 '''
     7 import socket
     8 import time
     9 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    10 client.connect(('127.0.0.1',8080))
    11 
    12 client.send('hello'.encode('utf-8'))
    13 time.sleep(5) # 解决了粘包 low
    14 client.send('我们'.encode('utf-8'))
    15 # 两个粘成一个包
    2.简单版 - 解决粘包

    strcut 模块的使用
     1 import struct
     2 import json
     3 res = struct.pack('i',429496)
     4 print(res,type(res),len(res))  # 数字转成了 bytes 型
     5 # b'xb8x8dx06x00' <class 'bytes'> 4
     6 
     7 # client.recv(4)
     8 obj = struct.unpack('i',res)
     9 print(obj)
    10 
    11 # res = struct.pack('i',1231213123123)  # 数据 不合理  若是发一个文件的话 就有可能 很大
    12 
    13 # q Q d 是8位  i l L 是4位
    14 res = struct.pack('d',120000223232123123123121231)
    15 print(res,type(res),len(res))
    16 # b'{x00x00x00' <class 'bytes'> 4
    参考: http://blog.csdn.net/w83761456/article/details/21171085
    http://www.cnblogs.com/linhaifeng/articles/6129246.html
     1 header_dic = {
     2     'filename': 'a.txt',
     3     'md5': 'xxxxxxx',
     4     'total_size': 1231231321321232132131232321321321321323221231312123123213213
     5 }
     6 header_json = json.dumps(header_dic)
     7 print(header_json,type(header_json))
     8 header_bytes = header_json.encode('utf-8')
     9 print(header_bytes,type(header_bytes))
    10 print(len(header_bytes))  # 会变
    11 
    12 res = struct.pack('i',len(header_bytes))  # 固定长度
    13 print(res,len(res))
    14 data = struct.unpack('i',res)
    15 print(data)

    服务端
     1 import subprocess
     2 import socket
     3 import struct
     4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     5 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 重用ip和端口 任然存在4次挥手的状态 解决办法
     6 phone.bind(('127.0.0.1',8080))
     7 phone.listen(0)
     8 print('strating...')
     9 while True:
    10     conn,client_addr = phone.accept()
    11     print(client_addr)
    12 
    13     while True:
    14         try:
    15             # 1.收命令
    16             cmd = conn.recv(8096) # 8096 一次接收完  # 系统规定不能超过8096个
    17             if not cmd:break
    18             print('客户端数据:',cmd)
    19 
    20             # 2.执行命令,拿到结果
    21             obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, # 客户端用 utf-8发的
    22                                    stdout=subprocess.PIPE,
    23                                    stderr=subprocess.PIPE)
    24             stdout = obj.stdout.read()
    25             stderr = obj.stderr.read()
    26 
    27             # 3.把命令的结果返回给客户端
    28             # 第一步:制作固定长度的报头
    29             print(len(stdout) + len(stderr))
    30             total_size = len(stdout) + len(stderr)
    31             header = struct.pack('i',total_size)
    32             # 第二部:把报头发给客户端
    33             conn.send(header)
    34             # 第三步:再发真实的数据
    35             # conn.send(stdout+stderr) # 有效率问题的 这里 之后 可以优化
    36             conn.send(stdout)
    37             conn.send(stderr)  # 这样会自动粘包 比 + 号的效率高
    38         except ConnectionResetError:
    39             break
    40     conn.close()
    41 
    42 phone.close()

    客户端
     1 '''
     2 粘包现象 解决了 仔细想想 是有问题的 报头里面 应该 包含对真实数据的描述 就不能这样了
     3 '''
     4 import socket
     5 import struct
     6 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     7 phone.connect(('127.0.0.1',8080))
     8 while True:
     9     # 1.发命令
    10     cmd = input('msg>>>:').strip()  # dir ls
    11     if not cmd:continue
    12     phone.send(cmd.encode('utf-8'))
    13 
    14     # 2.拿到命令的结果,并打印  8096 再大 就没 意义了
    15     # 第一步 先收报头 收到有用的信息
    16     obj = phone.recv(4)
    17     # total_size = 10241
    18     # 第二步:从报头中解析出对真实数据的描述 数据的长度
    19     total_size = struct.unpack('i', obj)[0]  # i 表示 整数
    20     # data = phone.recv(1024)   # 这里是个坑 有可能会大于1024 接收数据量的最大限制
    21     # 第三步:接收真实的数据
    22     # data = phone.recv(526)   # 这里是个坑 从自己的os的内存里 拿数据 不可能无限大 所以 那个数字不可能无限大
    23     # data=phone.recv(526)
    24     # data=phone.recv(526)
    25     recv_size = 0
    26     recv_data = b''
    27     while recv_size < total_size:
    28         res = phone.recv(526)
    29         recv_data += res
    30         recv_size+=len(res)
    31 
    32     print(recv_data.decode('gbk'))
    33 
    34     # print(data.decode('gbk'))   # linux:utf-8  windows:gbk
    35 
    36 phone.close()
    3.终极版 - 解决粘包

    服务端
     1 import subprocess
     2 import socket
     3 import struct
     4 import json
     5 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     6 phone.bind(('127.0.0.1',8080))
     7 phone.listen(5)
     8 print('strating...')
     9 while True:
    10     conn,client_addr = phone.accept()
    11     print(client_addr)
    12 
    13     while True:
    14         try:
    15             # 1.收命令
    16             cmd = conn.recv(8096) # 8096 一次接收完
    17             if not cmd:break
    18             print('客户端数据:',cmd)
    19 
    20             # 2.执行命令,拿到结果
    21             obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, # 客户端用 utf-8发的
    22                                    stdout=subprocess.PIPE,
    23                                    stderr=subprocess.PIPE)
    24             stdout = obj.stdout.read()
    25             stderr = obj.stderr.read()
    26 
    27             # 3.把命令的结果返回给客户端
    28 
    29             # 第一步:制作固定长度的报头  # 将字典转成 str 转成 bytes  用到了序列化
    30             header_dic = {
    31                 'filename':'a.txt',
    32                 'md5':'xxxxxxx',
    33                 'total_size': len(stdout)+len(stderr)
    34             }
    35             header_json = json.dumps(header_dic)
    36             header_bytes = header_json.encode('utf-8')   # 这里不知道 多长 会粘包!!
    37 
    38             # 第二步先发送报头的长度
    39             conn.send(struct.pack('i',len(header_bytes)))
    40 
    41             # 第三步:再发报头
    42             conn.send(header_bytes)
    43 
    44             # 第四部:在发真实的数据
    45             conn.send(stdout)
    46             conn.send(stderr)  # 这样会自动粘包 比 + 号的效率高
    47         except ConnectionResetError:
    48             break
    49     conn.close()
    50 
    51 phone.close()

    客户端
     1 '''
     2 思路:
     3     1.处理报头 准备发送的字典 先发报头的长度 再收报头 得到数据的长度 在收数据
     4     做字典 能容纳 很多信息量
     5     解决了:1.报头信息量少 2.i 格式有限的 解决了
     6 '''
     7 import json
     8 import socket
     9 import struct
    10 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    11 phone.connect(('127.0.0.1',8080))
    12 while True:
    13     # 1.发命令
    14     cmd = input('msg>>>:').strip()  # dir ls
    15     if not cmd:continue
    16     phone.send(cmd.encode('utf-8'))
    17 
    18     # 2.拿到命令的结果,并打印
    19     # 第一步 先收报头的长度
    20     obj = phone.recv(4)
    21     header_size = struct.unpack('i',obj)[0]
    22 
    23     # 第二步:在收报头
    24     header_bytes = phone.recv(header_size)
    25 
    26     # 第三步:从报头中解析出对真实数据的描述
    27     header_json = header_bytes.decode('utf-8')
    28     header_dic = json.loads(header_json)
    29     print(header_dic)
    30     total_size = header_dic['total_size']
    31 
    32     # 第四步:接收真实的数据
    33     recv_size = 0
    34     recv_data = b''
    35     while recv_size < total_size:
    36         res = phone.recv(526)
    37         recv_data += res
    38         recv_size+=len(res)
    39 
    40     print(recv_data.decode('gbk'))  # linux:utf-8  windows:gbk
    41 
    42 
    43 phone.close()



  • 相关阅读:
    javablogs
    Android学习笔记WIFI设备
    线程
    初次尝试Chrome扩展开发——以幻灯片方式显示网页内的图片
    could not find the main class, Program will exit(已解决)
    tomcat6.0配置(含配置视频下载)
    Windows下JDK1.6.0+Tomcat6.0的安装配置
    Java学习
    【翻译】在没有安装ASP.NET MVC3的服务器上运行ASP.NET MVC3的程序scottgu
    AForge.NET框架的使用
  • 原文地址:https://www.cnblogs.com/alice-bj/p/8611071.html
Copyright © 2011-2022 走看看