首页 > 其他分享 >(3)基于 TCP 协议实现服务端执行代码将结果反馈给客户端

(3)基于 TCP 协议实现服务端执行代码将结果反馈给客户端

时间:2023-06-23 17:34:16浏览次数:50  
标签:TCP server client msg size recv 服务端 客户端

基于 TCP 协议实现服务端执行代码将结果反馈给客户端

TCP协议是流式协议:在数据传输过程中大量数据的传入会造成数据的丢失和不完整

解决数据传输过程中的问题:自定义协议

应用:基于网络上传和下载文件

socketserver:基于模块实现并发

  • 服务端满足的条件
    • 一直对外提供服务
    • 并发的服务多个客户端

【一】传输过程1.0

(1)需求

  • 客户端发送需要执行的代码
  • 服务端接收到客户端传过来的代码
    • 服务端调用方法执行代码并拿到执行后的结果
    • 服务端将执行后的结果进行返回
  • 客户端接收到服务端返回的结果并做打印输出

(2)基础版1.1

  • 服务端
# -*-coding: Utf-8 -*-
# @File : 服务端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/22

# client.connect(('127.0.0.1', 8880))
# 客户端设置的 ip 和 port
from socket import *

# 执行命令模块
import subprocess

# 创建服务对象
server = socket(AF_INET, SOCK_STREAM)

# 建立链接桥梁
server.bind(('127.0.0.1', 8080))

# 指定半连接池大小
server.listen(5)

# 接收数据和发送数据
while True:
    # 从半连接池里面取出链接请求,建立双向链接,拿到连接对象
    # 拿到 建成的链接对象 和 客户端的 ip port
    conn, client_addr = server.accept()

    while True:
        # 检测可能会抛出的异常 并 对异常做处理
        try:
            # 基于 取出的链接对象 进行通信
            cmd_from_client = conn.recv(1024)

            # 不允许传过来的信息为空
            if len(cmd_from_client) == 0:
                break

            # 执行客户端传过来的命令
            # 接收执行命令的结果
            msg_server = subprocess.Popen(cmd_from_client.decode('utf-8'),  # 对命令进行解码
                                          shell=True,  # 执行shell命令
                                          stdout=subprocess.PIPE,  # 管道一
                                          stderr=subprocess.PIPE,  # 管道二
                                          )

            # 返回命令的结果  ---- 成功或失败  (Linux系统可以用utf-8解码,Windows系统需要用gbk解码)
            true_msg = msg_server.stdout.read()  # 读取到执行成功的结果 ---- 二进制数据类型
            false_msg = msg_server.stderr.read()  # 读取到执行失败的结果 ---- 二进制数据类型

            # 反馈信息给 发送信息的客户端
            conn.send(true_msg)
            conn.send(false_msg)

        except Exception as e:
            break
    conn.close()
  • 客户端
# -*-coding: Utf-8 -*-
# @File : 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/22

from socket import *

# 创建  socket 对象
client = socket(AF_INET, SOCK_STREAM)

# 创建链接 IP 和 端口
client.connect(('127.0.0.1', 8080))

while True:
    msg = input('enter msg :>>>').strip()

    # 输入的内容不能为空
    if len(msg) == 0: continue

    # 传输过程中的数据为二进制数据。对文本数据进行转码
    msg = msg.encode('utf-8')
    client.send(msg)

    # 接收来自服务端返回的结果
    msg_from_server = client.recv(1024)

    # 对服务端返回的信息进行解码(Mac/Linux解码用utf-8,Windows用GBK)
    msg_from_server = msg_from_server.decode('gbk')
    print(msg_from_server)

    client.close()
  • 问题:
    • 服务端:
      • 执行代码,代码为空会报错
      • 执行代码,返回的数据可能存在空/报错信息
    • 客户端:
      • 输入的指令长度,可能会超出范围
      • 接受到的服务端反馈的结果可能会特别多
      • 如何打印超出数据范围(缓存到系统里)的数据
  • 由此引出粘包问题:
    • 在 TCP 协议中是流式协议,数据是源源不断的传入到客户端中,但是客户端可以接受到的信息的长度是有限的
    • 当接收到指定长度的信息后,客户端进行打印输出
      • 剩余的其他数据会被缓存到 内存中
    • 当再次执行其他命令时
      • 新的数据的反馈结果,会叠加到上一次没有完全打印完全的信息的后面,造成数据的错乱
    • 当客户端想打印新的命令的数据时,打印的其实是上一次没有打印完的数据
      • 对数据造成的错乱
  • 如何解决?
    • 解决粘包问题:解决办法
    • (1) 拿到数据的总大小 recv_total_size
    • (2) recv_size = 0 ,循环接收,每接收一次,recv_size += 接收的长度
    • (3) 直到 recv_size = recv_total_size 表示接受信息完毕,结束循环

【二】粘包问题解决2.0

(1)粘包问题

  • 粘包问题出现的原因
    • TCP 协议是流式协议,数据像水流一样粘在一起,没有任何边界之分
    • 收数据没有接收干净,有残留,就会和下一次的结果混淆在一起
  • 解决粘包问题的核心法门就是
    • 每次都收干净
    • 不造成数据的混淆
发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,

当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的

因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。

而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。

例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

  • 此外,发送方引起的粘包是由TCP协议本身造成的

    • TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。

    • 若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

      • TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。

        • 收发两端(客户端和服务器端)都要有一一成对的socket
        • 因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块
        • 然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。
        • 即面向流的通信是无消息保护边界的。
      • UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。

        • 不会使用块的合并优化算法,
        • 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包
        • 在每个UDP包中就有了消息头(消息来源地址,端口等信息)
        • 这样,对于接收端来说,就容易进行区分处理了。
        • 即面向消息的通信是有消息保护边界的。

tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略

  • udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成
    • 若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠
  • tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。
    • 数据是可靠的,但是会粘包。

(2)为什么UDP协议不存在粘包问题

(2.1)UDP演示

  • UDP客户端

    # -*-coding: Utf-8 -*-
    # @File : udp客户端 .py
    # author: Chimengmeng
    # blog_url : https://www.cnblogs.com/dream-ze/
    # Time:2023/6/23
    import socket
    from socket import *
    
    # 创建client对象
    client = socket(AF_INET, SOCK_DGRAM)
    
    client.connect(('127.0.0.1', 8080))
    
    client.send(b'world')
    client.send(b'hello')
    
    msg_from_server = client.recvfrom(1024)
    print(msg_from_server)
    
  • UDP服务端

    # -*-coding: Utf-8 -*-
    # @File : udp服务端 .py
    # author: Chimengmeng
    # blog_url : https://www.cnblogs.com/dream-ze/
    # Time:2023/6/23
    
    from socket import *
    
    server = socket(AF_INET, SOCK_DGRAM)
    
    server.bind(('127.0.0.1', 8080))
    
    res_from_client = server.recvfrom(1024)
    
    print(res_from_client)
    # (b'world', ('127.0.0.1', 56852))
    
    res_from_client_two = server.recvfrom(1024)
    
    print(res_from_client_two)
    # (b'hello', ('127.0.0.1', 61798))
    
    res_from_client_three = server.recvfrom(5)
    
    print(res_from_client_three)
    # OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小。
    # 无法接收到 数据以外的数据 报错
    
  • 当我们启动udp服务端后,由udp客户端向服务端发送两条数据

    • 但是在udp服务端只接收到了一条数据
    • 这是因为 udp 是报式协议,传送数据过程中会将数据打包直接发走,不会对数据进行拼接操作(没有Nagle算法)

(2.2)TCP演示

  • tcp客户端

    # -*-coding: Utf-8 -*-
    # @File : tcp客户端 .py
    # author: Chimengmeng
    # blog_url : https://www.cnblogs.com/dream-ze/
    # Time:2023/6/23
    
    from socket import *
    
    client = socket(AF_INET, SOCK_STREAM)
    
    client.connect(('127.0.0.1', 8081))
    
    client.send(b'hello')
    client.send(b'world')
    
    msg_from_server = client.recv(1024)
    print(msg_from_server)
    # b'return'
    
    client.close()
    
  • tcp服务端

    # -*-coding: Utf-8 -*-
    # @File : tcp服务端 .py
    # author: Chimengmeng
    # blog_url : https://www.cnblogs.com/dream-ze/
    # Time:2023/6/23
    
    from socket import *
    
    server = socket(AF_INET, SOCK_STREAM)
    
    server.bind(('127.0.0.1', 8081))
    
    server.listen(3)
    
    conn, client_addr = server.accept()
    
    msg_from_client = conn.recv(1024)
    print(msg_from_client.decode('utf-8'))
    # helloworld
    
    conn.send(b'return')
    
    conn.close()
    
  • 从以上我们可以看到

    • TCP协议传输过程中将我们的两次发送的数据拼接成了一个发送到服务端

通过比较我们可知,udp协议虽然不存在粘包问题,但是,udp协议的安全性有待考量

【三】粘包问题解决3.0

利用 struct 模块将传输过去的数据的总长度 打包 + 到头部进行发送

  • 客户端
# -*-coding: Utf-8 -*-
# @File : 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/22

from socket import *

# 解指定数据长度
import struct

# 创建  socket 对象
client = socket(AF_INET, SOCK_STREAM)

# 创建链接 IP 和 端口
client.connect(('127.0.0.1', 8083))

while True:
    msg = input('enter msg :>>>').strip()

    # 输入的内容不能为空
    if len(msg) == 0:
        continue

    # 传输过程中的数据为二进制数据。对文本数据进行转码
    msg = msg.encode('utf-8')
    client.send(msg)

    # 接收来自服务端返回的结果

    # 解决粘包问题:解决办法
    # (1) 先收到固定长度的头,将头部解析到数据的描述信息,拿到数据的总大小 recv_total_size
    # 解析出接收到的总数据的长度
    recv_total_size_msg = client.recv(4)
    # 解包返回的是元祖。元祖第一个参数就是打包的数字
    recv_total_size = struct.unpack('i', recv_total_size_msg)[0]

    # (2) recv_size = 0 ,循环接收,每接收一次,recv_size += 接收的长度
    # (3) 直到 recv_size = recv_total_size 表示接受信息完毕,结束循环
    # 初始化数据长度
    recv_size = 0
    while recv_size < recv_total_size:
        # 本次接收 最多能接收 1024 字节的数据
        msg_from_server = client.recv(1024)
        # 本次接收到的打印的数据长度
        recv_size += len(msg_from_server)

        # 对服务端返回的信息进行解码(Mac/Linux解码用utf-8,Windows用GBK)
        msg_from_server = msg_from_server.decode('gbk')
        print(msg_from_server, end='')

    else:
        print('命令结束')

    client.close()
  • 服务端
# -*-coding: Utf-8 -*-
# @File : 服务端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/22

# client.connect(('127.0.0.1', 8880))
# 客户端设置的 ip 和 port
from socket import *

# 执行命令模块
import subprocess
# 将数据打包成指定4个长度的数据
import struct

# 创建服务对象
server = socket(AF_INET, SOCK_STREAM)

# 建立链接桥梁
server.bind(('127.0.0.1', 8083))

# 指定半连接池大小
server.listen(5)

# 接收数据和发送数据
while True:
    # 从半连接池里面取出链接请求,建立双向链接,拿到连接对象
    # 拿到 建成的链接对象 和 客户端的 ip port
    conn, client_addr = server.accept()

    while True:
        # 检测可能会抛出的异常 并 对异常做处理
        try:
            # 基于 取出的链接对象 进行通信
            cmd_from_client = conn.recv(1024)

            # 不允许传过来的信息为空
            if len(cmd_from_client) == 0:
                break

            # 执行客户端传过来的命令
            # 接收执行命令的结果
            msg_server = subprocess.Popen(cmd_from_client.decode('utf-8'),  # 对命令进行解码
                                          shell=True,  # 执行shell命令
                                          stdout=subprocess.PIPE,  # 管道一
                                          stderr=subprocess.PIPE,  # 管道二
                                          )

            # 返回命令的结果  ---- 成功或失败  (Linux系统可以用utf-8解码,Windows系统需要用gbk解码)
            true_msg = msg_server.stdout.read()  # 读取到执行成功的结果 ---- 二进制数据类型
            false_msg = msg_server.stderr.read()  # 读取到执行失败的结果 ---- 二进制数据类型

            # (1):先发头部信息(固定长度的bytes二进制数据):对数据信息的描述(包括数据的总长度)
            total_size_from_server = len(true_msg) + len(false_msg)
            # int类型  -----> 固定长度的 bytes
            # 参数 i 表示是整型,具体解释参考文档
            total_size_from_server_pack = struct.pack('i', total_size_from_server)
            conn.send(total_size_from_server_pack)

            # 反馈信息给 发送信息的客户端
            conn.send(true_msg)
            conn.send(false_msg)

        except Exception as e:
            break
    conn.close()
  • 客户端可以完美的接收到查出额定长度以外的数据

    同时这也是 自定义协议的 简单操作

【四】粘包问题最终版4.0

通过json模式 ---- 模版修改参数直接套用

  • 客户端
# -*-coding: Utf-8 -*-
# @File : 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/22
import json
from socket import *

# 解指定数据长度
import struct

# 创建  socket 对象
client = socket(AF_INET, SOCK_STREAM)

# 创建链接 IP 和 端口
client.connect(('127.0.0.1', 8085))

while True:
    msg = input('enter msg :>>>').strip()

    # 输入的内容不能为空
    if len(msg) == 0:
        continue

    # 传输过程中的数据为二进制数据。对文本数据进行转码
    msg = msg.encode('utf-8')
    client.send(msg)

    # 接收来自服务端返回的结果

    # (1.1) 先收四个字节的数据,从接收到的数据中解析出json格式的二进制数据的长度
    json_data_size_unpack = client.recv(4)

    # 解包返回的是元祖。元祖第一个参数就是打包的数字
    json_data_size = struct.unpack('i', json_data_size_unpack)[0]

    # (1.2) 对 服务端 返回的数据中指定长度进行截取 拿到 json 格式的二进制数据
    json_data_bytes = client.recv(json_data_size)

    # (1.3) 对指定数据进行json格式的解码并取出需要的信息
    header_dict_str = json_data_bytes.decode('utf-8')
    header_dict = json.loads(header_dict_str)

    # (1.4) 取出字典中的信息总长度
    recv_total_size = header_dict['total_size']

    # (2) 接收真实的数据
    # recv_size = 0 ,循环接收,每接收一次,recv_size += 接收的长度
    # (3) 直到 recv_size = recv_total_size 表示接受信息完毕,结束循环
    # 初始化数据长度
    recv_size = 0
    while recv_size < recv_total_size:
        # 本次接收 最多能接收 1024 字节的数据
        msg_from_server = client.recv(1024)
        # 本次接收到的打印的数据长度
        recv_size += len(msg_from_server)

        # 对服务端返回的信息进行解码(Mac/Linux解码用utf-8,Windows用GBK)
        msg_from_server = msg_from_server.decode('gbk')
        print(msg_from_server, end='')

    else:
        print('命令结束')

    client.close()
  • 服务端
# -*-coding: Utf-8 -*-
# @File : 服务端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/22

# client.connect(('127.0.0.1', 8880))
# 客户端设置的 ip 和 port
from socket import *

# 执行命令模块
import subprocess
# 将数据打包成指定4个长度的数据
import struct
# 将头部信息转成json格式(通用信息格式)
import json

# 创建服务对象
server = socket(AF_INET, SOCK_STREAM)

# 建立链接桥梁
server.bind(('127.0.0.1', 8085))

# 指定半连接池大小
server.listen(5)

# 接收数据和发送数据
while True:
    # 从半连接池里面取出链接请求,建立双向链接,拿到连接对象
    # 拿到 建成的链接对象 和 客户端的 ip port
    conn, client_addr = server.accept()

    while True:
        # 检测可能会抛出的异常 并 对异常做处理
        try:
            # 基于 取出的链接对象 进行通信
            cmd_from_client = conn.recv(1024)

            # 不允许传过来的信息为空
            if len(cmd_from_client) == 0:
                break

            # 执行客户端传过来的命令
            # 接收执行命令的结果
            msg_server = subprocess.Popen(cmd_from_client.decode('utf-8'),  # 对命令进行解码
                                          shell=True,  # 执行shell命令
                                          stdout=subprocess.PIPE,  # 管道一
                                          stderr=subprocess.PIPE,  # 管道二
                                          )

            # 返回命令的结果  ---- 成功或失败  (Linux系统可以用utf-8解码,Windows系统需要用gbk解码)
            true_msg = msg_server.stdout.read()  # 读取到执行成功的结果 ---- 二进制数据类型
            false_msg = msg_server.stderr.read()  # 读取到执行失败的结果 ---- 二进制数据类型

            # (1):先发头部信息(固定长度的bytes二进制数据):对数据信息的描述(包括数据的总长度)
            total_size_from_server = len(true_msg) + len(false_msg)

            # (2)自定义头部信息
            headers_dict = {
                'file_name': 'a.txt',
                'total_size': total_size_from_server,
                'md5': 'md5'
            }

            # 打包头部信息 - 将字典转成 json 格式数据类型
            json_data_str = json.dumps(headers_dict)
            # 将 json 格式数据转成二进制数据传输
            json_data_bytes = json_data_str.encode('utf-8')

            # int类型  -----> 将json格式的二进制数据打成固定长度的 bytes
            # 参数 i 表示是整型,具体解释参考文档
            json_data_size_pack = struct.pack('i', len(json_data_bytes))
            conn.send(json_data_size_pack)

            # 发送打包好的头信息
            conn.send(json_data_bytes)

            # 反馈信息给 发送信息的客户端
            conn.send(true_msg)
            conn.send(false_msg)

        except Exception as e:
            break
    conn.close()

标签:TCP,server,client,msg,size,recv,服务端,客户端
From: https://www.cnblogs.com/dream-ze/p/17499409.html

相关文章

  • 【十】认证客户端的链接合法性
    【十】认证客户端的链接合法性如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂那么利用hmac+加盐的方式来实现服务端#_*_coding:utf-8_*___author__='ly'fromsocketimport*importhmac,ossecret_key=b'lybangbangbang'defco......
  • Python基于Socket编写TcpServer通信基本框架
    如下主要是实现单客户端连接通信,如下为Socket模块的常用属性和方法介绍。如要实现多客户端连接,请使用threading模块的多线程技术实现。属性:•socket.AF_INET:IPv4地址族。•socket.AF_INET6:IPv6地址族。•socket.SOCK_STREAM:TCP协议类型。•socket.SOCK_DGRAM:UDP协议类......
  • 树莓派4B改造成云桌面客户端,连接DoraCloud免费版
     RaspberryPi(树莓派)是为学习计算机编程教育而设计的只有信用卡大小的微型电脑,自问世以来受众多计算机发烧友和创客的追捧,曾经一“派”难求。DoraCloud是一款多平台的桌面虚拟化管理软件,支持Hyper-V、VMware、Proxmox、XenServer等多种虚拟化平台。DoraCloud提供免费版,可以支......
  • tcpcopy + tcpdump 离线回放
    简单来说,就是用tcpdump记录线上请求,用tcpcopy来重放,如下图所示: ......
  • Subversion 1.8.9 ( SVN Client ) 安装最新版本的svn客户端 - svn 账户 密码
    http://www.fulingjiang.cn/linux/62.htmlForCentOS7Users: 1.[WandiscoSVN]2.name=WandiscoSVNRepo3.baseurl=http://opensource.wandisco.com/centos/5/svn-1.8/RPMS/$basearch/4.enabled=15.gpgcheck=06.yumcleanall7.......
  • fpga 以太网w5500 SPI传输80MHz FPGA verilog TCP客户端驱动源码,8个SOCKET都可用,SPI
    fpga以太网w5500SPI传输80MHzFPGAverilogTCP客户端驱动源码,8个SOCKET都可用,SPI频率80MHZ,硬件验证以通过。w5500ip核w5500软核,还有TCP服务端和UDP模式,联系联系我要那个,默认发TCP客户端。这个代码是用fpga驱动和使用w5500模块,做过优化,可能以达到w5500最高传输速度,学习必......
  • 设备Labview源码,给国内主机厂配套,采用Modus _tcp和西门子P L C通讯采集数据,研华P C I
    设备Labview源码,给国内主机厂配套,采用Modus_tcp和西门子PLC通讯采集数据,研华PCI板卡,工艺配方,数据曲线存储和追溯,是有志于上位机labview工程师参考好教程ID:27100606427875413......
  • 西门子、三菱、台达PLC手机组态软件,支持modbus协议的ModbusTesla手机组态软件 只支持m
    西门子、三菱、台达PLC手机组态软件,支持modbus协议的ModbusTesla手机组态软件只支持modbustcp,只要下位机支持标准的modbus协议就可以,不论PLC,变频器还是仪表等等。没有网口的可以加个串口服务器,软件版本1.37.6版本安卓手机组态软件可以和设备进行局域网通信,局域网通信时,只要安卓......
  • OPCServer DA OPC服务端opcserver发包)好用,几百个应用现场
    OPCServerDAOPC服务端opcserver发包)好用,几百个应用现场二次开发源代码,C#开发,可二次开发。已应用到多个行业的几百个应用现场,长时间运行稳定,可靠。本项目中提供测试OPCServer的软件开发源码,有详细的注释,并提供详细的使用说明,二次开发清晰明了。文件中提供测试过程视频,操作简单,......
  • 浅谈TCP和UDP
    简介在计算机网络中,TCP(传输控制协议)和UDP(用户数据报协议)是两个常用的传输层协议。它们分别提供了可靠的数据传输和快速的数据传送,成为互联网世界中的双子星。本文将探讨TCP和UDP的特点、优势和应用场景,以及如何选择合适的协议来满足不同的需求。TCP定义英文名:TransmissionCon......