基于TCP的Socket通讯

基于 TCP 的 Socket 通讯

最近要实现两个机器之间基于 TCP 的 socket 通讯(个人使用 Python 实现),尝试了官方的 demo 代码后总是被拒绝连接,仔细研究了一下并成功建立两台局域网内机器之间的通讯,通过两次请求的方式解决了粘包问题。

通讯建立

在 Python 语言中要实现 socket 通讯需要使用内置的 socket 包来实现,具体使用可以参考官方文档。

一般,通过下述代码建立一个 socket 通讯,其中第一个参数表示使用 ipv4 地址,第二个参数表示使用 TCP 协议族,该段调用会创建一个 Socket 对象,后续的绑定、连接监听都是基于这个 Socket 对象的。

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

服务端配置

首先,需要在服务端开启通讯监听,等待连接,这里为了演示方便,服务端返回的信息固定一个字符串。

import socket
import TCP_utils

# socket.AF_INET使用ipv4协议族
# socket.SOCK_STREAM使用tcp通讯
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口和地址
addr = ("172.18.129.245", 8990)  # 改为自己的服务器ip或者本地环回
sock.bind(addr)
# 监听接入访问的socket
sock.listen(10)
while True:
    print("server is waiting")
    # 接收消息
    conn, addr = sock.accept()  # 接受客户端连接
    data_head = TCP_utils.parse_header(conn)
    data_len = data_head['data_len']
    data = conn.recv(data_len)
    print("服务端收到消息:{}".format(data.decode("utf-8")))
    # 发送反馈
    msg = 'finish connect'
    head_len, head_bytes = TCP_utils.create_header('server', msg)
    conn.send(head_len)
    conn.send(head_bytes)
    conn.sendall(msg.encode('utf-8'))

    conn.close()

客户端配置

开启服务端的 socket 监听后就可以与其建立会话了,具体代码如下。

import socket
import TCP_utils


# 发送内容到服务器
while True:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    addr = ("192.168.43.27", 8990)
    sock.connect(addr)
    msg = input("发送消息:")
    head_len, head_info = TCP_utils.create_header("client1", msg)
    sock.send(head_len)
    sock.send(head_info)
    sock.sendall(msg.encode("utf-8"))
    # 接受反馈
    data_head = TCP_utils.parse_header(sock)
    data_len = data_head['data_len']
    data = sock.recv(data_len)
    print('收到消息', data.decode("utf-8"))
    # 关闭链接通路
    sock.close()

粘包问题

该问题指的是是客户端和服务端进行数据传输时,接收的一方不知道消息之间的界限(即不能准确判断不一次性提取多少字节的数据)。该问题本质上是由于 TCP 协议本身的缺陷,为了提高效率,TCP 发送消息时必须等到发送方收集到足够多的消息,若每次信息都很少会合成一个 TCP 段进行发送。

该问题解决也不难,先发送一个报文头部信息来指明后面的数据的字节数,该头部信息包含了发送方 id 和数据段长度(字节数),接收方通过数据段长度接受指定字节数信息。为了控制发送的成功,将头部信息通过 struct 模块的打包方法压缩为固定 4 字节。

具体的解析和封装头部信息的代码封装为 TCP_utils 模块,代码如下。

import struct
import json


def create_header(id, data):
    """
    客户端编号
    :param id:
    :param data:
    :return:
    """
    data_len = len(data)
    head_info = {'id': id, 'data_length': data_len}
    head_json = json.dumps(head_info)
    head_bytes = head_json.encode('utf-8')
    head_bytes_len = len(head_bytes)
    head_length = struct.pack('i', head_bytes_len)
    return head_length, head_bytes


def parse_header(conn):
    """
    建立的连接
    :param conn:
    :return:
    """
    head_info = conn.recv(4)
    head_length = struct.unpack('i', head_info)[0]  # 元组格式
    data_head = conn.recv(head_length).decode('utf-8')
    data_head = json.loads(data_head)
    return data_head

效果演示

开启服务端监听,随后,通过客户端建立一个连接 ,进行会话,演示如下图。

基于TCP的Socket通讯_第1张图片

你可能感兴趣的:(Python)