zoukankan      html  css  js  c++  java
  • python应用之socket编程

    tcp/udp下的socket的基本使用

    基于tcpsocket

    Tcp是基于链接的,必须先启动服务端,然后启动客户端进行链接

    服务端:

    ss = socket() #创建服务器套接字
    ss.bind()      #把地址绑定到套接字
    ss.listen()      #监听链接
    inf_loop:      #服务器无限循环
        cs = ss.accept() #接受客户端链接
        comm_loop:         #通讯循环
            cs.recv()/cs.send() #对话(接收与发送)
        cs.close()    #关闭客户端套接字
    ss.close()        #关闭服务器套接字(可选)

    客户端:

    cs = socket()    # 创建客户套接字
    cs.connect()    # 尝试连接服务器
    comm_loop:        # 通讯循环
         cs.send()/cs.recv()    # 对话(发送/接收)
    cs.close()            # 关闭客户套接字

    简单的实现:

    这里是单个的 一次通信

    mport socket           #   AF_INET 基于网络通信, SOCK_STREAM(基于流的,tcp)
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #   买手机
    phone.bind(('127.0.0.1', 8000)) #   绑定手机卡
    phone.listen(5) #   开机(监听)  #   5表示 最多有5个可以发送连接, 放在链接池中
    print('--->')
    conn, addr = phone.accept()  #   (等电话,等待连接,会阻塞)(触发的是3次握手)
    
    #  收发消息(在tcp协议中触发的是数据传输)
    # 注意收发信息不是用手机, 而是conn这条连接
    mesg = conn.recv(1024)  #   收消息
    print('客户端发来的消息是:',mesg)
    conn.send(mesg.upper())   # 发消息
    
    #   断开链接  (在tcp协议中触发的是4次挥手)
    conn.close()
    
    #   关机
    phone.close()
    服务端
    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 买手机
    phone.connect(('127.0.0.1', 8000))  # 拨通电话 (触发的是三次握手)
    
    #   收发数据
    # phone.send('hello'.encode('utf-8'))   # socket不支持字符串发送,仅支持字节编码发送所以要用
    phone.send(bytes('hello', encoding='utf-8'))  # 这两个发送一样,只要转换成二进制就行
    data = phone.recv(1024)
    print('收到服务端发来的消息', data)
    客户端

    这个是简单的交互模式

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 18-5-14 下午5:56
    # @Author  : LK
    # @File    : fu_ceshi.py
    # @Software: PyCharm
    from socket import *
    buffersize = 1024
    #
    tcp_server = socket(AF_INET, SOCK_STREAM)  #   流式套接字
    tcp_server.bind(('127.0.0.1', 8000))
    tcp_server.listen(5)
    
    #   为了接收多个连接
    while True:
        #   等待连接
        conn, add = tcp_server.accept()  #  等待连接 ,会阻塞
        print('双向连接是:',conn)
        print('客户端地址是',add)
    
        #   收发信息
        while True:
            # try:
            #   这个异常实在win中,但是在linux下有时候是死循环, 因为客户端突然断掉,conn就没了,mesg一直是空
                mesg = conn.recv(buffersize)
                if not mesg:
                    print('客户端你断开连接了')
                    break
                print('服务端接收的信息:',mesg.decode('utf-8'))
                conn.send(mesg.upper())
                print('服务端以发送回去')
            # except Exception:
            #     break
        conn.close()
    #   关闭连接
    
    tcp_server.close()
    服务端
    from socket import *
    buffersize=1024
    tcp_client = socket(AF_INET, SOCK_STREAM)
    #   主动连接
    tcp_client.connect(('127.0.0.1', 8000))
    
    #   收发信息
    while True:
        send_mesg = input('请输入要个服务端发送的信息,break停止').strip('')
        #   如果发送的是空格,重新发送
        if not send_mesg: continue
        if send_mesg == 'break':
            print('客户端停止发送信息了')
            break
        else:
            tcp_client.send(send_mesg.encode('utf-8'))
            print('客户端已发送消息')
    
        mesg = tcp_client.recv(buffersize)
        print('客户端接受的信息:',mesg.decode('utf-8'))
    
    #   关闭连接
    tcp_client.close()
    客户端

    基于udpsocket

    udp是无链接的,先启动哪一端都不会报错

    服务端:
    ss = socket()   #创建一个服务器的套接字
    ss.bind()       #绑定服务器套接字
    inf_loop:       #服务器无限循环
         cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
    ss.close()                         # 关闭服务器套接字
    
    客户端
    cs = socket()   # 创建客户套接字
    comm_loop:      # 通讯循环
        cs.sendto()/cs.recvfrom()   # 对话(发送/接收)
    cs.close()                      # 关闭客户套接字

    udp基本实现:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 18-5-15 下午1:38
    # @Author  : LK
    # @File    : udp_服务端.py
    # @Software: PyCharm
    
    from socket import *
    ip_port = (('127.0.0.1', 8080))
    buffsize = 1024
    udp_server = socket(AF_INET, SOCK_DGRAM) #  数据报套接字
    udp_server.bind(ip_port)
    while True:
        data, addr = udp_server.recvfrom(buffsize)
        print('客户端发来的消息',data.decode('utf-8'))
        udp_server.sendto(data.upper(), addr)   #   注意这里是addr
    udp_server.close()
    udp服务端
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 18-5-15 下午1:38
    # @Author  : LK
    # @File    : udp_客户端.py
    # @Software: PyCharm
    from socket import  *
    ip_port = (('127.0.0.1', 8080))
    buffsize = 1024
    udp_client = socket(AF_INET, SOCK_DGRAM) #  数据包套接字
    
    while True:
        try:
            mesg = input('>>>').strip()
            udp_client.sendto(mesg.encode('utf-8'), ip_port)
            #   recvfrom 接受的是一个元祖, 里面是信息和对方地址
            data, addr = udp_client.recvfrom(buffsize)
            print('服务端发来的是', data.decode('utf-8'))
            # print(data)
        except KeyboardInterrupt as e:
            print(e)
            print('程序异常中断')
            break
    
    
    
    
    
    #   recv 在自己这端的缓冲区为空时,会阻塞(返回的是信息)
    #   recvfrom 不会阻塞 (返回是一个元祖,分别是接受的信息和地址)
    
    #   tcp没有实现并发,而udp默认实现了,因为udp不用建立连接,直接网端口发送,而tcp需要建立连接
    udp客户端

    udp的客户端可以开多个,

     常用函数

    服务端套接字函数
    s.bind() 绑定(主机,端口号)到套接字
    s.listen() 开始TCP监听
    s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

    客户端套接字函数
    s.connect() 主动初始化TCP服务器连接
    s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

    公共用途的套接字函数
    s.recv() 接收TCP数据
    s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    s.recvfrom() 接收UDP数据
    s.sendto() 发送UDP数据
    s.getpeername() 连接到当前套接字的远端的地址
    s.getsockname() 当前套接字的地址
    s.getsockopt() 返回指定套接字的参数
    s.setsockopt() 设置指定套接字的参数
    s.close() 关闭套接字

    面向锁的套接字方法
    s.setblocking() 设置套接字的阻塞与非阻塞模式
    s.settimeout() 设置阻塞套接字操作的超时时间
    s.gettimeout() 得到阻塞套接字操作的超时时间

    面向文件的套接字的函数
    s.fileno() 套接字的文件描述符
    s.makefile() 创建一个与该套接字相关的文件

    tcp粘包问题

     

    首先要知道socket的收发消息的原理

    send 是发送给用 对方后,存在对方的内核态中,

    recv 收消息是从自己的缓存中收

    只有tcp有粘包现象,udp没有,但是udp会发生丢包问题, 就是发的数据太多,到那时udp的缓冲区,只能接收固定大小,剩下的就丢了, 所以udp没有tcp可靠

    流程就是, 服务端发送数据给自己的缓冲区,然后通过网络发送给客户端的缓冲区,
    客户端从自己的缓冲区进行取数据,recv函数 一次可能取不完,就可能会出现粘包问题
    如果recv取的少的话,或者发送的过多,就可能会出现粘包问题
    Recv 是从自己的 内核态中去数据, 如果数据过多的话就会出现粘包问题
    解决粘包问题思路: 可以先获取消息的大小, 然后死循环一直发,知道发送到指定大小在停止, 收包也是如此

    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

    粘包的表现

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 18-5-16 上午8:42
    # @Author  : LK
    # @File    : tcp_粘包问题_服务端.py
    # @Software: PyCharm
    
    #   粘包问题发生的原因:
    # 第一种情况:tcp在发送的时候,基于流的方式发送数据,发送端要等缓冲区满后才发送出去,造成粘包
    # (发送数据的时间很短,数据很小,就会和在一起发送)
    
    #   第二种情况,发送端发送的数据很大,但是接收端的缓冲区已经放不下,直接从缓冲区里面拿
    # 多次都从缓冲区里面取数据(就是发送的大,但是取的少)
    
    #   解决方法:一般是先发送一个数据大小,然后回复一下,在用循环发送数据,高级办法(struct),跟高级(偏函数)
    
    from socket import  *
    tcp_socket = socket(AF_INET, SOCK_STREAM)
    ip_port = (('127.0.0.1', 8080))
    tcp_socket.bind(ip_port)
    tcp_socket.listen(5)
    conn, addr = tcp_socket.accept()
    print('连接的信息',conn)
    print('详细信息',addr)
    #   收发数据
    while True:
        # '''第二种粘包现象,就是发的多,每次收的少'''
        # mesg = conn.recv(2)
        # print('第一次接受的',mesg.decode('utf-8'))
        # mesg = conn.recv(1)
        # print('第二次接受的',mesg.decode('utf-8'))
        # mesg = conn.recv(1)
        # print('第三次接受的',mesg.decode('utf-8'))
    
        '''第一种粘包现象, 发的少收得多'''
        mesg = conn.recv(1024)
        print('第一次接受的',mesg.decode('utf-8'))
        mesg = conn.recv(1)
        print('第二次接受的',mesg.decode('utf-8'))
        mesg = conn.recv(1)
        print('第三次接受的',mesg.decode('utf-8'))
    
    coon.close()
    tcp_socket.close()
    粘包的表现_服务端
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 18-5-16 上午8:41
    # @Author  : LK
    # @File    : tcp_粘包问题_客户端.py
    # @Software: PyCharm
    
    from socket import *
    tcp_client = socket(AF_INET, SOCK_STREAM)
    ip_port = (('127.0.0.1',8080))
    tcp_client.connect(ip_port)
    
    #   收发消息
    while  True:
        '''第一中粘包现象,发的少,收的多'''
        tcp_client.send('第一次发送的'.encode('utf-8'))
        tcp_client.send('第二次发送的'.encode('utf-8'))
        tcp_client.send('第三次发送的'.encode('utf-8'))
        tcp_client.recv(4)
    
    
        # '''第二种粘包现象,发的多收的少'''
        # tcp_client.send(b'hhhh')
        # tcp_client.send(b'ff')
        # tcp_client.recv(4)
    粘包的表现_客户端

    图解模式:

    粘包的本质:接收端不知道,从缓存中取多少

    第一种粘包现象:

    接收端

    发送端

    运行结果

    第二种粘包现象

    接收端

    发送端

    结果

     tcp模拟终端,shell执行结果来显示 粘包问题并解决

    如果是linux下 ,不用修改, 输入ls 显示当前目录下的文件,然后输入ifconfig 查看ip等信息,返回的信息比较多,一次不能提取完,再输入其他命令就会产生错误

    windows 中 如果产生编码错误就把utf-8 改成gbk  

    命令用win下的 如 dir  然后 ipconfig 再输入 其他的

    #   存在粘包问题
    
    import subprocess
    from socket import *
    from tcp_模拟终端处理数据 import dealwith
    
    ip_port = (('127.0.0.1', 9000))
    buffsize = 1024
    tcp_server = socket(AF_INET, SOCK_STREAM)
    try :
        tcp_server.bind(ip_port)
        tcp_server.listen(5)
        print('还没有人连接')
        #   等待多个连接
        while True:
            try:
                conn, addr = tcp_server.accept()
                print('客户端连接信息', conn)
                print('客户端地址', addr)
    
                #   收发消息
                while True:
                    mesg = conn.recv(buffsize)
                    if not mesg:
                        print('客户端中断连接了,服务器等待下次连接')
                        break
                    print('客户端发送的是', mesg.decode('utf-8'))
    
                    data = dealwith(mesg)
                    #   将这些封装成了一个函数
                    '''
                    # # 处理从客户端接收的信息
                    # #   res是接收的信息,经过shell脚本处理后的结果
                    # res = subprocess.Popen(mesg.decode('utf-8'), shell=True,
                    #                        stdin=subprocess.PIPE,
                    #                        stdout=subprocess.PIPE,
                    #                        stderr=subprocess.PIPE
                    #                        )
                    # #   获取错误信息
                    # err = res.stderr.read().decode('utf-8')
                    # if not err:
                    #     data = res.stdout.read().decode('utf-8')
                    # else:
                    #     data = err
                    '''
    
                    conn.sendall(data)
                    print('服务端已发送')
            except KeyboardInterrupt as e:
                print('外部强制结束')
                break
    
            conn.close()
    except OSError as e:
        print('端口以用过')
    
    tcp_server.close()
    模拟终端_产生粘包_服务端
    #   存在粘包问题
    
    from socket import *
    tcp_client = socket(AF_INET, SOCK_STREAM)
    ip_port = (('127.0.0.1', 9000))
    buffsize = 1024
    tcp_client.connect(ip_port)
    
    while True:
        # mesg = input('>>>').strip()
        #   去除左右空格
        mesg = input('>>>').lstrip().rstrip()
        if not mesg: continue
        tcp_client.send(mesg.encode('utf8'))
        print('客户端已发送')
        data = tcp_client.recv(buffsize)
        
        if not data:
            print('服务端发来的是空信息')
            continue
        print('服务端发来的信息是:',data.decode('utf-8'))
    
    tcp_client.close()
    模拟终端_产生粘包_客户端
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 18-5-15 下午5:23
    # @Author  : LK
    # @File    : tcp_模拟终端处理数据.py
    # @Software: PyCharm
    import subprocess
    def dealwith(mesg):
        # 处理从客户端接收的信息
            #   res是接收的信息,经过shell脚本处理后的结果
            res = subprocess.Popen(mesg.decode('utf-8'), shell=True,
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE
                                   )
            #   获取错误信息
            err = res.stderr.read()
            if not err:
                data = res.stdout.read()
                if not data:
                    # data = '你输入的数据返回值为空'.encode('utf-8')
                    return '你输入的数据返回值为空'.encode('utf-8')
            else:
                data = err
            return data
    #   这里返回的是一个字节的形式
    
    # res = subprocess.Popen('ls', shell=True,
    #                        stdin=subprocess.PIPE,
    #                        stdout=subprocess.PIPE,
    #                        stderr=subprocess.PIPE
    #                        )
    # # stdin=subprocess.PIPE,   标准输入, 输入的内容会放在管道中
    # # stdout=subprocess.PIPE,  标准输出,就是结果会放在pipe这个管道中
    # # stderr=subprocess.PIPE,  标准错误,如果结果错误 会放在这个管道中
    #
    # #   读取管道内容
    # # print(res.stdout.read()), 读取之后,管道里面的就没有输出了
    数据处理

    第一种处理粘包的方法:

    思路: 在发送端首先发送给接受端一个4个字节大小的数据,内容是要发的真是数据的大小.接收端在取数据的时候取4个字节,

    然后给发送端发送一个准备接受的信号,当发送端接收到这个信号时,开始发送信息.接收端开始循环接收信息.

    struct的用法

    import subprocess
    from socket import *
    from tcp_模拟终端处理数据 import dealwith
    
    ip_port = (('127.0.0.1', 9000))
    buffsize = 1024
    tcp_server = socket(AF_INET, SOCK_STREAM)
    try:
        tcp_server.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
        tcp_server.bind(ip_port)
        tcp_server.listen(5)
        print('还没有人连接')
        #   等待多个连接
        while True:
            try:
                conn, addr = tcp_server.accept()
                print('客户端连接信息', conn)
                print('客户端地址', addr)
    
                #   收发消息
                while True:
                    mesg = conn.recv(buffsize)
                    if not mesg:
                        print('客户端中断连接了,服务器等待下次连接')
                        break
                    print('客户端发送的是', mesg.decode('utf-8'))
    
                    data = dealwith(mesg)
                    #   解决粘包:,发送端先发送一个消息的大小,接收端回复一个准备接收的信号,然后发送端开始循环发送信息
                    length = len(data)
                    conn.send(str(length).encode('utf-8'))
                    read_send = conn.recv(buffsize).decode('utf-8')
    
                    #   注意这里的问题,就是发送一个大的文件,用sendall,,如何发送,还是在于如何取
                    if read_send == 'read_recv':
                        conn.sendall(data)
                        print('服务端已发送')
    
            except KeyboardInterrupt as e:
                print('外部强制结束')
                break
    
            conn.close()
    except OSError as e:
        print('端口以用过')
        # tcp_server.close()
    tcp_server.close()
    解决粘包问题_low版_服务端
      # low版解决粘包问题
    from socket import *
    import sys
    
    tcp_client = socket(AF_INET, SOCK_STREAM)
    ip_port = (('127.0.0.1', 9000))
    buffsize = 1024
    
    tcp_client.connect(ip_port)
    #3  用connect_ex()比较好, 可以重复利用 端口
    while True:
    
        #   去除左右空格
        mesg = input('>>>').lstrip().rstrip()
        if not mesg: continue
        tcp_client.send(mesg.encode('utf8'))
        print('客户端已发送')
    
        data_lenth = int(tcp_client.recv(1024).decode('utf-8'))
    
        #   解决粘包问题
        tcp_client.send('read_recv'.encode('utf-8'))
        recv_leng = 0
        data = b''
        while recv_leng < data_lenth:
            if data_lenth - recv_leng > buffsize:
                data += tcp_client.recv(buffsize)
                recv_leng = len(data)
            else:
                #   当剩余的数据 小于缓冲区大小时
                data += tcp_client.recv(data_lenth - recv_leng)
                recv_leng = len(data)
    
        if not data:
            print('服务端发来的是空信息')
            continue
        print('服务端发来的信息是:', data.decode('utf-8'))
    
    tcp_client.close()
    解决粘包问题_low版_客户端

    第二种处理粘包方法:推荐使用

    思路struct,发送端用struct.pack()  (一般用int类型占4个字节) 将要发送的数据的大小打包后发送过去,然后在发送真实数据.两个必然会粘包.

    但是接收端在接收的时候先接收4个字节,然后用struct.unpack()解包,得到数据大小,再利用循环接受真实数据.

    struct用法:

    http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
    # 用struct 解决粘包
    import struct
    import subprocess
    from socket import *
    from tcp_模拟终端处理数据 import dealwith
    
    ip_port = (('127.0.0.1', 9000))
    buffsize = 1024
    tcp_server = socket(AF_INET, SOCK_STREAM)
    #   这一句是 重复利用 端口
    
    try:
        tcp_server.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
        tcp_server.bind(ip_port)
        tcp_server.listen(5)
        print('还没有人连接')
        #   等待多个连接
        while True:
            try:
                conn, addr = tcp_server.accept()
                print('客户端连接信息', conn)
                print('客户端地址', addr)
    
                #   收发消息
                while True:
                    mesg = conn.recv(buffsize)
                    if not mesg:
                        print('客户端中断连接了,服务器等待下次连接')
                        break
                    print('客户端发送的是', mesg.decode('utf-8'))
    
                    data = dealwith(mesg)
                    #   解决粘包:,发送端先发送一个消息的大小,接收端回复一个准备接收的信号,然后发送端开始循环发送信息
                    length = len(data)
                    conn.send(struct.pack('i', length))
                    conn.sendall(data)
                    print('服务端已发送')
    
            except KeyboardInterrupt as e:
                print('外部强制结束')
                break
    
            conn.close()
    except OSError as e:
        print('端口以用过')
        # tcp_server.close()
    tcp_server.close()
    解决粘包-服务端_推荐使用
    #   struct解决粘包
    from socket import *
    import  struct
    tcp_client = socket(AF_INET, SOCK_STREAM)
    ip_port = (('127.0.0.1', 9000))
    buffsize = 1024
    tcp_client.connect(ip_port)
    
    while True:
        # mesg = input('>>>').strip()
        #   去除左右空格
        mesg = input('>>>').lstrip().rstrip()
        if not mesg: continue
        tcp_client.send(mesg.encode('utf8'))
        print('客户端已发送')
    
        #   解决粘包
        #   服务端会发送过来一个 数据的大小,和 一个真实数据的包,两个必定会粘包,但是解包是分开来解,先解4个字节(对方发来的数据包大小)
        data_length = tcp_client.recv(4)
        length = struct.unpack('i', data_length)[0]
    
        #   循环接收数据
        recv_leng = 0
        data = b''
        while recv_leng < length:
            if length - recv_leng > buffsize:
                data += tcp_client.recv(buffsize)
                recv_leng = len(data)
            else:
                data += tcp_client.recv(length-recv_leng)
                recv_leng = len(data)
    
        if not data:
            print('服务端发来的是空信息')
            continue
        print('服务端发来的信息是:', data.decode('utf-8'))
    
    tcp_client.close()
    解决粘包-客户端

     udp 的丢包问题 

    #   udp传输数据是以数据报的方式发送,每次发送都是发送完整的数据,但是接收方接收的可能小于发送放,就会产生丢包
    # 因为每次都是发送一个完整的,取时也是取完缓冲区,所以不会发生粘包,但是会发生丢包,意味着,这是不安全的
    from socket import *
    udp_server = socket(AF_INET, SOCK_DGRAM)
    ip_port = (('127.0.0.1',8080))
    udp_server.bind(ip_port)
    
    #   收发消息
    while True:
    
        #   这里的6 出现两个汉字是因为, 汉字的编码方式不同(3个字节,在win中不同)
        data, addr = udp_server.recvfrom(6)
        print('第一次接收的信息是:',data.decode('utf-8'))
        data, addr = udp_server.recvfrom(6)
        print('第二次接收的信息是',data.decode('utf-8'))
        # udp_server.sendto(data,addr)
    服务端
    from socket import  *
    udp_client = socket(AF_INET, SOCK_DGRAM)
    ip_port = (('127.0.0.1', 8080))
    buffsize = 1024
    
    while True:
        data = '第一次发送的信息'
        udp_client.sendto(data.encode('utf-8'), ip_port)
        data = '第二次发送的信息'
        udp_client.sendto(data.encode('utf-8'), ip_port)
        udp_client.recvfrom(10)
    客户端

    socket_server实现并发

    基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环

    socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)

    #   这样就tcp服务器就能被多个用户链接了, 客户端流程不变
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            # self.request # 相当于  conn
            # self.client_address   #  相当于 addr
            while True:
                '''在这里写收发消息'''
    
    s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)  # 将上面的类实例化一个
    s.serve_forever()  #    一直开启状态

    tcp_socket_server并发版_服务端

    import socketserver
    import struct
    from 封装处理粘包_拆包 import UnPacket, SendData
    
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            self.request
            print('连接人的信息')
            print('conn是', self.request)  # conn
            print('addr是', self.client_address)  # addr
    
            while True:
                '''收发消息'''
                head_data = self.request.recv(4)
                if not head_data: break
                real_data = UnPacket(head_data, self.request)
                print('客户端发来的是:%s客户端地址是是%s' % (real_data.decode('utf-8'), self.client_address))
    
                #   回复客户端,防止粘包
                send_data = 'aaaaaaaa'
                SendData(send_data, self.request)
    
    
    if __name__ == '__main__':
        # pass
        print('还没有人连接')
        s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)  # 多线程
        # s = socketserver.ForkingTCPServer(('127.0.0.1', 8080), MyServer)  # 多进程win中不行
    
        #   服务器一直开着
        s.serve_forever()

    客户端

    from socket import  *
    from 封装处理粘包_拆包 import UnPacket, SendData
    import struct
    tcp_server = socket(AF_INET, SOCK_STREAM)
    ip_port = ('127.0.0.1', 8080)
    buffsize = 1024
    tcp_server.connect_ex(ip_port)
    
    while True:
        '''收发信息'''
        mesg = input('>>>').strip()
        #   防止粘包处理函数
        SendData(mesg, tcp_server)
        mesg= tcp_server.recv(4)
        if not mesg: break
        recv_data = UnPacket(mesg, tcp_server)
        print('服务端发送的信息是',recv_data.decode('utf-8'))
    tcp_server.close()

    处理粘包的问题,封装了一下后是: 

    封装处理粘包_拆包
    import struct
    #   传过来包的头部信息,和连接,返回接受的数据是一个byte类型
    def UnPacket(head_data ,conn):
        buffsize = 4
        '''解决粘包,需要传送过数据头,和连接'''
        real_data_length = struct.unpack('i',head_data)[0]
        real_data = b''
        recv_length = 0
    
        #   这个少了点优化
        # while recv_length < real_data_length:
        #     real_data += conn.recv(1024)
        #     recv_length = len(real_data)
    
        while recv_length < real_data_length:
            if real_data_length - recv_length > buffsize:
                real_data += conn.recv(buffsize)
                recv_length = len(real_data)
            else:
                real_data += conn.recv(real_data_length-recv_length)
                recv_length = len(real_data)
    
        return real_data
    
    #   接受要发送的数据,是字符串,和要发送的链接(客户端发送tcp_server, 服务端发送conn或self.request))
    def SendData(real_data,conn):
        '''发送包,发两次,'''
        head_length = struct.pack('i', len(real_data))  #   后面是一个整数,所以是四个字节, 这里有个优化
        conn.send(head_length)
        conn.sendall(real_data.encode('utf-8'))
        print('客户端发送成功')
  • 相关阅读:
    预警:亚马逊出售的监控摄像机存在预装恶意软件
    opendaylight+openvswitch环境部署
    keepalived+nginx实现高可用
    Huawei ipv6 bgp配置
    F5配置http跳转https
    F5配置ssl卸载
    IBGP路由重分布进IGP路由
    H3C NQA 配置
    Cisco N7K第三方光模块的使用
    Cisco C3850交换机重启后配置无法保存的故障处理
  • 原文地址:https://www.cnblogs.com/xiaokang01/p/9057372.html
Copyright © 2011-2022 走看看