python网络编程socket相关

网络编程

socket案例

服务端

点击查看代码
import socket
 ​
 # 1.监听本机的IP和端口
 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 sock.bind(('127.0.0.1', 8001))  # 127.0.0.1 或 查看自己局域网本地IP地址
 sock.listen(5)
 ​
 while True:
     # 2.等待,有人来连接(阻塞)
     conn, addr = sock.accept()
     print("有人来连接了...")
 ​
     # 3.连接成功后立即发送
     conn.sendall("欢迎使用xx系统,请输入您想要办理的业务!".encode("utf-8"))
 ​
     while True:
         # 3.等待接受信息
         data = conn.recv(1024)
         if not data:
             break
         data_string = data.decode("utf-8")
 ​
         # 4.回复消息
         conn.sendall("你说啥?".encode("utf-8"))
     print("断开连接了")
     # 5.关闭与此人的连接
     conn.close()
 ​
 # 6.停止服务端程序
 sock.close()
 ​

客户端

点击查看代码
import socket
 ​
 # 1. 向指定IP发送连接请求
 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 client.connect(('127.0.0.1', 8001))
 ​
 # 2.连接成功后,获取系统登录信息
 message = client.recv(1024)
 print(message.decode("utf-8"))
 ​
 while True:
     content = input("请输入(q/Q退出):")
     if content.upper() == 'Q':
         break
     client.sendall(content.encode("utf-8"))
 ​
     # 3. 等待,消息的回复
     reply = client.recv(1024)
     print(reply.decode("utf-8"))
 ​
 # 关闭连接,关闭连接时会向服务端发送空数据。
 client.close()
  • IO多路复用+非阻塞

默认程序是阻塞的 当程序加上sock.setblocking(False) # 加上就变为了非阻塞,非阻塞的代码一般与IO多路复用结合,可以迸发出更大的作用。

粘包

  • 什么是粘包

    两台电脑在进行收发数据时,其实不是直接将数据传输给对方。

    • 对于发送者,执行 sendall/send 发送消息时,是将数据先发送至自己网卡的 写缓冲区 ,再由缓冲区将数据发送给到对方网卡的读缓冲区。

    • 对于接受者,执行 recv 接收消息时,是从自己网卡的读缓冲区获取数据。

    所以,如果发送者连续快速的发送了2条信息,接收者在读取时会认为这是1条信息,即:2个数据包粘在了一起。

  • 解决粘包问题

    每次发送的消息时,都将消息划分为 头部(固定字节长度) 和 数据 两部分。例如:头部,用4个字节表示后面数据的长度。

    • 发送数据,先发送数据的长度,再发送数据(或拼接起来再发送)。

    • 接收数据,先读4个字节就可以知道自己这个数据包中的数据长度,再根据长度读取到数据。

    对于头部需要一个数字并固定为4个字节,这个功能可以借助python的struct包来实现:

     import struct
     ​
     # ########### 数值转换为固定4个字节,四个字节的范围 -2147483648 <= number <= 2147483647  ###########
     v1 = struct.pack('i', 199)
     print(v1)  # b'\xc7\x00\x00\x00'
     ​
     for item in v1:
         print(item, bin(item))
     ​
     # ########### 4个字节转换为数字 ###########
     v2 = struct.unpack('i', v1) # v1= b'\xc7\x00\x00\x00'
     print(v2) # (199,)

  • 服务端

    点击查看代码
    import socket
     import struct
     ​
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     sock.bind(('127.0.0.1', 8001))
     sock.listen(5)
     conn, addr = sock.accept()
     ​
     # 固定读取4字节
     header1 = conn.recv(4)
     data_length1 = struct.unpack('i', header1)[0] # 数据字节长度 21
     has_recv_len = 0
     data1 = b""
     while True:
         length = data_length1 - has_recv_len
         if length > 1024:
             lth = 1024
         else:
             lth = length
         chunk = conn.recv(lth) # 可能一次收不完,自己可以计算长度再次使用recv收取,指导收完为止。 1024*8 = 8196
         data1 += chunk
         has_recv_len += len(chunk)
         if has_recv_len == data_length1:
             break
     print(data1.decode('utf-8'))
     ​
     # 固定读取4字节
     header2 = conn.recv(4)
     data_length2 = struct.unpack('i', header2)[0] # 数据字节长度
     data2 = conn.recv(data_length2) # 长度
     print(data2.decode('utf-8'))
     ​
     conn.close()
     sock.close()
  • 客户端

    点击查看代码
    import socket
     import struct
     ​
     client = socket.socket()
     client.connect(('127.0.0.1', 8001))
     ​
     # 第一条数据
     data1 = 'alex正在吃'.encode('utf-8')
     ​
     header1 = struct.pack('i', len(data1))
     ​
     client.sendall(header1)
     client.sendall(data1)
     ​
     # 第二条数据
     data2 = '翔'.encode('utf-8')
     header2 = struct.pack('i', len(data2))
     client.sendall(header2)
     client.sendall(data2)
     ​
     client.close()

     

posted @ 2021-09-02 16:43  wq512  阅读(25)  评论(0编辑  收藏  举报