day30——socket套接字(完全版)

day30

基于TCP协议的socket循环通信

server
import socket
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen()
conn, addr = phone.accept()

from_client_data = conn.recv(1024)
print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")

conn.close()
phone.close()
client
import socket
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
data = input("请输入>>>")
phone.send(data.encode("utf-8"))
phone.close()

基于TCP协议的socket 链接+循环 通信

代码功能:服务器不会断开,客户端可以断开(且不能输入空字符串)

server
import socket
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen(3)
while 1:
    conn, addr = phone.accept()
    print(f"{addr}客户端连接了")
    while 1:
        try:
            from_client_data = conn.recv(1024)
            if from_client_data.decode("utf-8").upper() == "Q":
                print(f"{addr}客户端通信关闭!")
                break
            else:
                print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
            while True:
                data = input("请输入>>>")
                if not data:
                    print("输入内容不为空!")
                    continue
                else:
                    break
            conn.send(data.encode("utf-8").upper())
        except Exception:
            print(f"{addr}客户端通信关闭!")
            break
    conn.close()
phone.close()
clicent
import socket
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
while 1:
    try:
        data = input("请输入>>>")
        if not data:
            print("输入内容不为空!")
            continue
        phone.send(data.encode("utf-8"))
        if data.upper() == "Q":
            print("通信关闭!")
            break

        from_server_data = phone.recv(1024)
        if from_server_data.decode("utf-8").upper() == "Q":
            print("通信关闭!")
            break
        else:
            print(f"来自服务器的消息:{from_server_data.decode('utf-8')}")
    except Exception:
        print("通信关闭!")
        break
phone.close()

基于TCP协议的socket通信:实例:远程执行命令

bates:网络传输,文件存储时

server
import socket
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen(3)
while 1:
    conn, addr = phone.accept()
    print(f"{addr}客户端连接了")
    while 1:
        try:
            from_client_data = conn.recv(1024)
            import subprocess
            obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   )
            conn.send((obj.stdout.read() + obj.stderr.read()).upper())
        except Exception:
            print(f"{addr}客户端通信关闭!")
            break
    conn.close()
phone.close()
client
import socket
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
while 1:
    try:
        data = input("请输入>>>")
        if not data:
            print("输入内容不为空!")
            continue
        phone.send(data.encode("utf-8"))
        if data.upper() == "Q":
            print("通信关闭!")
            break

        from_server_data = phone.recv(1024)
        print(from_server_data.decode('gbk'))
    except Exception:
        print("通信关闭!")
        break
phone.close()

粘包现象

为什么出现粘包?

只有TCP有粘包现象,UDP永远不会粘包!

操作系统的缓冲区

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

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

什么情况下出现粘包?

  • 出现粘包的情况

    • 客户端发送了一段数据过大,服务端只收了一部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包

    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()
    #             print(f'总字节数:{len(result)}')
    #             conn.send(result)
    #         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(300)  # 最多接受1024字节
    
    # # print(f'{from_server_data.decode("gbk")}')
    
    # print(len(from_server_data))
    
    # phone.close()
    
    • 连续短暂的send多次(数据量很小),你的数据会统一发送出去

server

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()```python

client

  ```python
  # 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算法

深入研究收发

不一定是一收一发

  • 发多次收一次

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")}')

# 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算法
  • 发一次收多次

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(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'hello world')
# phone.close()

如何解决粘包现象

解决粘包现象的思路:

服务端发一次数据 10000字节

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

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

    你发送总具体数据之前,先给我发一个总数据长度:5000个字节,如何在发送总数居

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

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

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

你要将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模块

服务端:
import struct
total_size = len(result)
print(f'总字节数:{total_size}')
head_bytes = struct.pack('i',total_size)
conn.send(head_bytes)

conn.send(result)
total_size int类型

客户端:
head_bytes = phone.recv(4)
total_size = struct.unpack('i',head_bytes)[0]

low版解决粘包现象

server
import socket
import subprocess
import struct
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen(3)
while 1:
    conn, addr = phone.accept()
    print(f"{addr}客户端连接了")
    while 1:
        try:
            from_client_data = conn.recv(1024)
            obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   )
            data = obj.stdout.read() + obj.stderr.read()
            data_len = len(data)
            print(f"长度:{len(data)}")
            conn.send(struct.pack("i", data_len))
            conn.send(data)
        except Exception:
            print(f"{addr}客户端通信关闭!")
            break
    conn.close()
phone.close()
client
import socket
import struct
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
while 1:
    data = input("请输入>>>")
    if not data:
        print("输入内容不为空!")
        continue
    phone.send(data.encode("utf-8"))
    if data.upper() == "Q":
        print("通信关闭!")
        break

    data_deposit = b""
    data_head = phone.recv(4)
    data_new = struct.unpack("i", data_head)[0]
    while len(data_deposit) < data_new:
        data_deposit += phone.recv(1024)
    print(data_deposit.decode("gbk"))
phone.close()

原文地址:https://www.cnblogs.com/NiceSnake/p/11385856.html