socket

Day30 socket

  1. socket套接字

    ​ socket处于应用层与传输层之间,提供了一些简单的接口. 避免与操作系统之间对接,省去了相当繁琐复杂的操作.

    ​ socket在python中属于一个模块.为我们提供了这些简单的功能.

  2. 单个客户端与服务端通信(low版)

    server

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8088))
    phone.listen(3)
    
    conn,addr = phone.accept()
    
    from_client_data = conn.recv(1024)
    print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
    
    to_client_data = input('>>>')
    conn.send(to_client_data.encode('utf-8'))
    conn.close()
    phone.close()

    client

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8088))
    
    to_server_data = input('>>>')
    phone.send(to_server_data.encode('utf-8'))
    
    from_server_data = phone.recv(1024)
    print(f'收到来自服务端的信息:{from_server_data.decode("utf-8")}')
    phone.close()
  3. 基于TCP协议的socket单个循环通信

    server:

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8088))
    phone.listen(3)
    
    conn,addr = phone.accept()
    while 1:
        try:
            from_client_data = conn.recv(1024)
            print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
    
            to_client_data = input('>>>')
            conn.send(to_client_data.encode('utf-8'))
        except ConnectionResetError:
            print('客户端异常断开链接')
            break
    conn.close()
    phone.close()

    client:

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8088))
    
    while 1:
            to_server_data = input('>>>')
            phone.send(to_server_data.encode('utf-8'))
    
            from_server_data = phone.recv(1024)
            print(f'收到来自服务端的信息:{from_server_data.decode("utf-8")}')
    phone.close()
  4. 基于TCP协议的socket 链接+循环 通信

    server

    import socket
    
    phone = socket.socket()
    
    phone.bind(('127.0.0.1',8848))
    
    phone.listen(2)
    # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
    
    while 1:
        conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
        print(f'链接来了: {conn,addr}')
    
        while 1:
            try:
                from_client_data = conn.recv(1024)  # 最多接受1024字节
    
                if from_client_data.upper() == b'Q':
                    print('客户端正常退出聊天了')
                    break
    
                print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
                to_client_data = input('>>>').strip().encode('utf-8')
                conn.send(to_client_data)
            except ConnectionResetError:
                print('客户端链接中断了')
                break
        conn.close()
    phone.close()
    
    
    

    client

    import socket
    
    phone = socket.socket()
    
    phone.connect(('127.0.0.1',8848))
    while 1:
        to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
        if not to_server_data:
            # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
            print('发送内容不能为空')
            continue
        phone.send(to_server_data)
        if to_server_data.upper() == b'Q':
            break
        from_server_data = phone.recv(1024)  # 最多接受1024字节
        print(f'来自服务端消息:{from_server_data.decode("utf-8")}')
    
    phone.close()
    
    
  5. 基于TCP协议的socket通信: 实例: 远程执行命令

    server

    import socket
    import subprocess
    phone = socket.socket()
    
    phone.bind(('127.0.0.1',8848))
    
    phone.listen(2)
    # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
    
    while 1:
        conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
        print(f'链接来了: {conn,addr}')
    
        while 1:
            try:
    
                from_client_data = conn.recv(1024)  # 最多接受1024字节
    
    
                if from_client_data.upper() == b'Q':
                    print('客户端正常退出聊天了')
                    break
    
                obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
    
                                       )
                result = obj.stdout.read() + obj.stderr.read()
    
                conn.send(result)
            except ConnectionResetError:
                print('客户端链接中断了')
                break
        conn.close()
    phone.close()
    
    
    
    
    # shell: 命令解释器,相当于调用cmd 执行指定的命令。
    # stdout:正确结果丢到管道中。
    # stderr:错了丢到另一个管道中。
    # windows操作系统的默认编码是gbk编码。
    
    

    client

    import socket
    
    phone = socket.socket()
    
    phone.connect(('127.0.0.1',8848))
    while 1:
        to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
        if not to_server_data:
            # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
            print('发送内容不能为空')
            continue
        phone.send(to_server_data)
        if to_server_data.upper() == b'Q':
            break
        from_server_data = phone.recv(1024)  # 最多接受1024字节
        print(f'{from_server_data.decode("gbk")}')
    
    phone.close()
    
    
  6. 粘包现象

    展示收发的问题

    server

    # 发多次收一次
    # import socket
    #
    # phone = socket.socket()
    #
    # phone.bind(('127.0.0.1',8848))
    #
    # phone.listen(5)
    #
    #
    # conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
    #
    # from_client_data = conn.recv(1024)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    #
    # from_client_data = conn.recv(1024)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    # conn.close()
    # phone.close()
    
    
    
    # 发一次收多次
    
    # import socket
    #
    # phone = socket.socket()
    #
    # phone.bind(('127.0.0.1',8848))
    #
    # phone.listen(5)
    #
    #
    # conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
    #
    # from_client_data = conn.recv(3)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    #
    # from_client_data = conn.recv(3)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    #
    # from_client_data = conn.recv(3)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    #
    # from_client_data = conn.recv(3)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    #
    # conn.close()
    # phone.close()
    

    client

    # 发多次收一次
    # import socket
    #
    # phone = socket.socket()
    #
    # phone.connect(('127.0.0.1',8848))
    #
    #
    # phone.send(b'he')
    # phone.send(b'llo')
    #
    #
    # phone.close()
    # Nigle算法
    
    
    # 发一次收多次
    # import socket
    #
    # phone = socket.socket()
    #
    # phone.connect(('127.0.0.1',8848))
    #
    #
    # phone.send(b'hello world')
    #
    #
    # phone.close()
    
    

    现象

    server

    # 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
    # import socket
    # import subprocess
    # phone = socket.socket()
    #
    # phone.bind(('127.0.0.1',8848))
    #
    # phone.listen(2)
    # # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
    #
    # while 1:
    #     conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
    #     # print(f'链接来了: {conn,addr}')
    #
    #     while 1:
    #         try:
    #
    #             from_client_data = conn.recv(1024)  # 最多接受1024字节
    #
    #
    #             if from_client_data.upper() == b'Q':
    #                 print('客户端正常退出聊天了')
    #                 break
    #
    #             obj = subprocess.Popen(from_client_data.decode('utf-8'),
    #                                    shell=True,
    #                                    stdout=subprocess.PIPE,
    #                                    stderr=subprocess.PIPE,
    #
    #                                    )
    #             result = obj.stdout.read() + obj.stderr.read()
    #             print(f'总字节数:{len(result)}')
    #             conn.send(result)
    #         except ConnectionResetError:
    #             print('客户端链接中断了')
    #             break
    #     conn.close()
    # phone.close()
    
    
    
    # s1 = '太白jx'
    # # print(len(s1))
    # b1 = s1.encode('utf-8')
    # # print(b1)
    # print(len(b1))
    
    
    '''
             客户端                          服务端
    
    第一次:  ipconfig                       317字节
             300个字节                       17个字节
    
    
             客户端                          服务端
    
    第二次:   dir                           376字节
              17字节                        376字节
    
    
    '''
    
    
    # 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
    
    # import socket
    #
    # phone = socket.socket()
    #
    # phone.bind(('127.0.0.1',8848))
    #
    # phone.listen(5)
    #
    #
    # conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
    #
    # from_client_data = conn.recv(1024)  # 最多接受1024字节
    # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
    # conn.close()
    # phone.close()
    
    
    # 展示一些收发的问题。
    

    client

    # import socket
    #
    # phone = socket.socket()
    #
    # phone.connect(('127.0.0.1',8848))
    # while 1:
    #     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
    #     if not to_server_data:
    #         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
    #         print('发送内容不能为空')
    #         continue
    #     phone.send(to_server_data)
    #     if to_server_data.upper() == b'Q':
    #         break
    #     from_server_data = phone.recv(300)  # 最多接受1024字节
    #     # print(f'{from_server_data.decode("gbk")}')
    #     print(len(from_server_data))
    #
    # phone.close()
    
    
    
    # 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
    
    # import socket
    #
    # phone = socket.socket()
    #
    # phone.connect(('127.0.0.1',8848))
    #
    #
    # phone.send(b'he')
    # phone.send(b'll')
    # phone.send(b'o')
    #
    #
    # phone.close()
    # Nigle算法
    
  7. 操作系统的缓存区

    1. 为什么存在缓冲区??

      1. 暂时存储一些数据.
      2. 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速.

      缺点: 造成了粘包现象之一.

  8. 什么情况下出现粘包

    1. 出现粘包的情况

      连续短暂的send多次(数据量很小),你的数据会统一发送出去.

    2. 深入研究收发

  9. 如何解决粘包现象

    解决粘包现象的思路:

    服务端发一次数据 10000字节,

    客户端接收数据时,循环接收,每次(至多)接收1024个字节,直至将所有的字节全部接收完毕.将接收的数据拼接在一起,最后解码.

    1. 遇到的问题: recv的次数无法确定.

      你发送总具体数据之前,先给我发一个总数据的长度:5000个字节。然后在发送总数据。

      客户端: 先接收一个长度。 5000个字节。

      然后我再循环recv 控制循环的条件就是只要你接受的数据< 5000 一直接收。

    2. 遇到的问题: 总数据的长度转化成的字节数不固定

      服务端:
      conn.send(total_size) 
      
      conn.send(result)
      total_size int类型
      
      
      客户端:
      total_size_bytes = phone.recv(4)
      total_size
      data = b''
      while len(data) < total_size:
        data = data + phone.recv(1024)
      
      

      你要将total_size int类型转化成bytes类型才可以发送

      387 ---- > str(387) '387' ---->bytes b'387' 长度 3bytes

      4185 ----> str(4185) '4185' ---->bytes b'4185' 长度 4bytes

      18000------------------------------------------------------> 长度 5bytes

      我们要解决:

      将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来。

      struct模块

  10. low版解决粘包现象

    server

    # 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
    import socket
    import subprocess
    import struct
    phone = socket.socket()
    
    phone.bind(('127.0.0.1',8848))
    
    phone.listen(2)
    # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
    
    while 1:
        conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
        # print(f'链接来了: {conn,addr}')
    
        while 1:
            try:
    
                from_client_data = conn.recv(1024)  # 接收命令
    
    
                if from_client_data.upper() == b'Q':
                    print('客户端正常退出聊天了')
                    break
    
                obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
    
                                       )
                result = obj.stdout.read() + obj.stderr.read()
                total_size = len(result)
                print(f'总字节数:{total_size}')
    
                # 1. 制作固定长度的报头
                head_bytes = struct.pack('i',total_size)
    
                # 2. 发送固定长度的报头
                conn.send(head_bytes)
    
                # 3. 发送总数据
                conn.send(result)
            except ConnectionResetError:
                print('客户端链接中断了')
                break
        conn.close()
    phone.close()
    
    
    
    
    # import struct
    
    

    client

    import socket
    import struct
    phone = socket.socket()
    
    phone.connect(('127.0.0.1',8848))
    while 1:
        to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
        if not to_server_data:
            # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
            print('发送内容不能为空')
            continue
        phone.send(to_server_data)
        if to_server_data.upper() == b'Q':
            break
    
        # 1. 接收报头
        head_bytes = phone.recv(4) 
        # 2. 反解报头
        total_size = struct.unpack('i',head_bytes)[0]
    
        total_data = b''
    
        while len(total_data) < total_size:
            total_data += phone.recv(1024)
    
        print(len(total_data))
        print(total_data.decode('gbk'))
    
    phone.close()
    
posted on 2019-08-16 08:16  七橼77 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/-777/p/11361614.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值