Python中Socket编程(TCP、UDP)

1. TCP协议下的如何解决粘包问题

  TCP(transport control protocol 传输控制协议)  使用Nagle算法,将多次间隔较小且数据量小的数据,合并成大的数据块;接受端无法识别每条数据的边界,因此产生粘包现象。

"""
Server
"""
from socket import *

back_log = 5
bufsize = 1024
ip_port = ('127.0.0.1', 8080)

tcp_server = socket(AF_INET, SOCK_STREAM)  # 数据流
tcp_server.bind(ip_port)
tcp_server.listen(back_log)

while True:
    print('服务端开始处理连接。。。')
    conn, addr = tcp_server.accept()
    print('客户端连接', conn)
    print('客户端地址', addr)
    while True:
        try:
            data = conn.recv(bufsize)
            if not data:
                break
            print('接收到的数据', data)
            conn.send(data.upper())
        except Exception as e:
            print(e)
            break
"""
Client
"""
from socket import *

bufsize = 1024
ip_port = ('127.0.0.1', 8080)

tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)
tcp_client.send('hello'.encode('utf-8'))
tcp_client.send('world'.encode('utf-8'))
response = tcp_client.recv(bufsize)
print('response is ==> ', response)  # response is ==>  b'HELLOWORLD'

解决思路:告知接收端数据长度,导入struct模块,将字节长度封装成4个字节发送给接收方;

服务端 =====》TCP实现远程操作命令

from socket import *
import subprocess,struct

bufsize = 1024
back_log = 5
ip_port = ('127.0.0.1', 8080)

tcp_server = socket(AF_INET, SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(back_log)
while True:
    conn, addr = tcp_server.accept()
    print('current connection', conn)
    while True:
        try:
            cmd = conn.recv(bufsize)
            if not cmd:
                break
            print('data from client', cmd)

            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stderr=subprocess.PIPE,
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE)
            cmd_res = res.stderr.read()
            if not cmd_res:
                cmd_res = res.stdout.read()
            print('response is:', cmd_res)

            data_length = struct.pack('i', len(cmd_res))
            conn.send(data_length)
            conn.send(cmd_res)
        except Exception as e:
            print(e)
            break
    # conn.close()

客户端 ====》

from socket import *
import struct

bufsize = 100
ip_port = ('127.0.0.1', 8080)
tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
    cmd = input('>>>> ').strip()
    if not cmd:
        continue
    if cmd == 'quit':
        break
    tcp_client.send(cmd.encode('utf-8'))

    length_data = tcp_client.recv(4)

    length = struct.unpack('i', length_data)[0]
    print(length)
    response = b''
    recsize = 0
    while recsize < length:
        response += tcp_client.recv(bufsize)
        recsize = len(response)
        # print('execute result is:', response)  # 接受字节长度小于发送数据长度,产生粘包
    print('execute result is:', response.decode('gbk'))
tcp_client.close()

2. TCP协议如何实现多个客户端连接(并发处理)

导入socketserver模块,实现ocketserver.BaseRequestHandler,重写 handle 方法

使用

ThreadingTCPServer,线程实现并发
import socketserver


class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print('conn is', self.request)  # conn
        print('addr is', self.client_address)  # addr

        while True:
            try:
                data = self.request.recv(1024)
                if not data:
                    break
                self.request.sendall(data.upper())
            except Exception as e:
                print(e)
                break


if __name__ == '__main__':
    ip_port = ('127.0.0.1', 8080)
    s = socketserver.ThreadingTCPServer(ip_port, MyServer)
    s.serve_forever()


3. 基于UDP套接字

  UDP 与TCP 不同,使用socket时用的时 SOCK_DGRAM 数据报

  sendto()发送的是元组数据类型,包含数据以及连接

  recvfrom()接收的也是数据以及连接

服务端 ====》

from socket import *

ip_port = ('127.0.0.1', 8080)
buf_size = 1024

udp_server = socket(AF_INET, SOCK_DGRAM)  # datagram 数据报
udp_server.bind(ip_port)

while True:
    data, addr = udp_server.recvfrom(buf_size)
    print(data)
    udp_server.sendto(data.upper(), addr)

客户端====》

from socket import *

ip_port = ('127.0.0.1', 8080)
buf_size = 1024

udp_client = socket(AF_INET, SOCK_DGRAM)  # datagram 数据报

while True:
    msg = input('==>: ').strip()
    udp_client.sendto(msg.encode('utf-8'), ip_port)
    response, addr = udp_client.recvfrom(buf_size)
    print(response.decode('utf-8'))
原文地址:https://www.cnblogs.com/louiszh/p/12391742.html