《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字

《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字

  • 《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
    • 基本概念
      • 服务端
      • 客户端
    • 基于 Linux 平台的 "Hello world!" 服务端和客户端
    • 基于 Linux 的文件操作
      • 打开文件
      • 关闭文件
      • 写文件
      • 读文件
    • 基于 Windows 平台的实现
      • Winsock 的初始化
      • 注销 Winsock 相关库
      • 基于 Windows 的套接字相关函数
      • 创建基于 Windows 的服务端和客户端
      • 基于 Windows 的 I/O 函数
    • 习题
      • (1)套接字在网络编程中的作用是什么?为什么称它为套接字?
      • (2)在服务器端创建套接字后,会依次调用listen函数和accept函数。请比较并说明两者作用。
      • (3)Linux中,对套接字数据进行I/O时可以直接使用I/O相关函数;而在Windows中则不可以。原因为何?
      • (4)创建套接字后一般会给它分配地址,为什么?为了完成地址分配需要调用哪些函数?
      • (5)Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。
      • (6)底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别?

《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字

基本概念

网络编程是什么?

编写程序使两台联网的计算机相互交换数据。

服务端

步骤 1:创建套接字

int socket(int domain, int type, int protocol);

成功时返回文件描述符,失败时返回-1。

步骤 2:绑定IP地址和端口号

int bind(int socket, const struct sockaddr *address, socklen_t address_len);

成功时返回0,失败时返回-1

步骤 3:使转化为可接受请求状态

int listen(int sockfd, int backlog);

成功时返回文件描述符,失败时返回-1。

步骤 4:受理请求连接

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

成功时返回文件描述符,失败时返回-1。

客户端

步骤 1:创建socket

步骤 2:发起连接请求

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

成功时返回0,失败返回-1。

基于 Linux 平台的 “Hello world!” 服务端和客户端

服务端程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>void error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];int str_len;if(argc!=3){printf("Usage:%s <IP> <port>\n",argv[0]);exit(1);}sock = socket(PF_INET,SOCK_STREAM,0);if(sock==-1){error_handling("socket() error!");}memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port=htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){error_handling("connect() error!");}str_len = read(sock,message,sizeof(message)-1);if(str_len==-1){error_handling("read() error!");}printf("Message from server:%s \n",message);close(sock);return 0;
}void error_handling(char* message)
{fputs(message,stderr);fputc('\n',stderr);exit(1);
}

客户端程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>void error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];int str_len;if(argc!=3){printf("Usage:%s <IP> <port>\n",argv[0]);exit(1);}sock = socket(PF_INET,SOCK_STREAM,0);if(sock==-1){error_handling("socket() error!");}memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port=htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){error_handling("connect() error!");}str_len = read(sock,message,sizeof(message)-1);if(str_len==-1){error_handling("read() error!");}printf("Message from server:%s \n",message);close(sock);return 0;
}void error_handling(char* message)
{fputs(message,stderr);fputc('\n',stderr);exit(1);
}

基于 Linux 的文件操作

Linux中一切皆文件,socket自然也是文件。

​ 每当生成文件或socket,操作系统都将返回分配给它们的整数(文件描述符),文件描述符只不过是为了方便的称呼操作系统创建的文件或socket而赋予的数。

​ 分配给标准输入,输出及标准错误的文件描述符。

文件描述符对象
0标准输入 :Standard Input
1标准输出 :Standard Ouput
2标准错误 :Standard Error

其他的文件描述符从3开始的。

打开文件

#include <sys/stat.h>
#include <fcntl.h>int open(const char *path, int fla);

成功时返回文件描述符,失败时返回-1。

参数:

  • path: 文件名的字符串地址

  • flag: 文件打开模式信息,如需传递多个参数,应通过 | 运算符组合。

打开模式含义
O_CREAT必要时创建文件
O_TRUNC删除全部现有数据
O_APPEND维持现有数据,保存到其后面
O_RDONLY只读打开
O_WRONLY只写打开
O_RDWR读写打开

关闭文件

#include <unistd.h>int close(int fildes);

成功时返回0,失败时返回-1。

参数:

  • fildes: 需要关闭的文件或socket的文件描述符

写文件

#include <unistd.h>ssize_t write(int fildes, const void *buf, size_t nbyte);

成功时返回写入的字节数,失败时返回-1。

参数:

  • fildes: 显示数据传输对象的文件描述符

  • buf:保存要传输数据的缓冲地址值

  • nbyte: 要传输数据的字节数

size_t : unsigned int,ssize_t:signed int,这些都是元数据类型,由操作系统定义,通过 typedef 声明。

示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(void)
{int fd;char buf[] = "Let's go!\n";fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC);if (fd == 1)error_handling("open() error!");printf("file descriptor: %d\n", fd);if (write(fd, buf, sizeof(buf)) == -1)error_handling("write() error!");close(fd);return 0;
}

运行结果:

在这里插入图片描述

注意:Windows 端可以用 type 命令打印 txt 文件的内容。

读文件

#include <unistd.h>ssize_t read(int fildes, void *buf, size_t nbyte);

成功时返回接收的字节数(但遇到文件结尾则返回0),失败时返回-1。

参数:

  • fildes:显示数据接收对象的文件描述符

  • buf:要保存接收数据的缓冲地址值

  • nbytes:要接收数据的最大字节数

示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>#define BUF_SIZE 100void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(void)
{int fd;char buf[BUF_SIZE];fd = open("data.txt", O_RDONLY);if (fd == -1)error_handling("open() error!");printf("file descriptor: %d\n", fd);if (read(fd, buf, BUF_SIZE) == -1)error_handling("read() error!");printf("file data: %s", buf);close(fd);return 0;
}

运行结果:

在这里插入图片描述

基于 Windows 平台的实现

Windows 套接字,简称 Winsock,大部分参考 BSD 系列 UNIX 套接字设计,和 Linux 套接字类似。

Winsock 的初始化

进行Winsock编程时,首先调用 WSAStartup 函数,设置程序中用到的Winsock版本,并初始化相应版本的库。

int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

参数:

  • wVersionRequested:Winsock版本信息
  • lpWSAData:WSADATA结构体变量的地址值

Winsock中存在多个版本,应准备WORD(typedef unsigned int WORD;)类型的套接字版本信息,若版本为1.2,则1是主版本号,2是副版本号,应传递 0x0201

​ 0x0201 高8位为副版本号,低8位为主版本号。这里借助 MAKEWORD 宏函数来传递版本信息号。

MAKEWORD(1,2); // 主版本号是1,副版本号是2,返回0x0201
int main(int argc,char* argv[])
{WSADATA wsaData;....if(WSAStartup(MAKEWORD(1,2), &wsaData) != 0){....}return 0;
}

注销 Winsock 相关库

#include <Winsock2.h>int WSACleanup(void);

成功时返回0,失败时返回 SOCKET_ERROR。

基于 Windows 的套接字相关函数

创建socket:

SOCKET socket(int af,int type,int protocol);

成功时返回套接字句柄(对应linux中的文件描述符),失败返回 INVALID_SOCKET。

绑定IP和端口号:

int bind(SOCKET s,const struct sockaddr* name,int namelen);

成功返回0,失败返回SOCKET_ERROR。

将服务端设置为可接受请求状态:

int listen(SOCKET s,int backlog);

成功返回0,失败返回SOCKET_ERROR。

受理请求连接(accept):

SOCKET accept(SOCKET s,struct sockaddr* addr,int* addrlen);

成功时返回套接字句柄,失败时返回INVALID_SOCKET。

从客户端发起请求连接:

int connect(SOCKET s,const struct sockaddr* name,int namelen);

成功时返回0,失败时返回SOCKET_ERROR。

关闭套接字:

int closesocket(SOCKET s);

成功时返回0,失败时返回SOCKET_ERROR。

Windows中的句柄相当于linux中的文件描述符,但是Windows中的文件句柄和套接字句柄是有区别的。

创建基于 Windows 的服务端和客户端

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServerSock, hClientSock;SOCKADDR_IN serverAddr, clientAddr;int szClientAddr;char message[] = "Hello World!";if (argc != 2){printf("Usage: %s <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() 	error!");hServerSock = socket(PF_INET, SOCK_STREAM, 0);if (hServerSock == INVALID_SOCKET)ErrorHanding("socket() 	error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);serverAddr.sin_port = htons(atoi(argv[1]));if (bind(hServerSock, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)ErrorHanding("bind() error!");if (listen(hServerSock, 5) == SOCKET_ERROR)ErrorHanding("listen() error!");szClientAddr = sizeof(clientAddr);hClientSock = accept(hServerSock, (SOCKADDR *)&clientAddr, &szClientAddr);if (hClientSock == INVALID_SOCKET)ErrorHanding("accept() error!");send(hClientSock, message, sizeof(message), 0);closesocket(hClientSock);closesocket(hServerSock);WSACleanup();return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN serverAddr;char message[30];int strLen;if (argc != 3){printf("Usage: %s <IP> <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() 	error!");hSocket = socket(PF_INET, SOCK_STREAM, 0);if (hSocket == INVALID_SOCKET)ErrorHanding("socket() 	error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(argv[1]);serverAddr.sin_port = htons(atoi(argv[2]));if (connect(hSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)ErrorHanding("connect() error!");strLen = recv(hSocket, message, sizeof(message) - 1, 0);if (strLen == -1)ErrorHanding("recv() error!");printf("Message from server: %s\n", message);closesocket(hSocket);WSACleanup();return 0;
}

编译遇到报错:

在这里插入图片描述

参考 【三种解决方法】undefined reference to `__imp_WSAStartup‘ 给出的解决办法,手动添加编译参数 -lwsock32:

gcc hello_server_win.c -lwsock32 -o hServerWingcc hello_client_win.c -lwsock32 -o hClientWin

编译就可以成功了:

在这里插入图片描述

我们运行服务端程序,监听 9190 端口:

hServerWin 9190

再运行客户端程序:

hClientWin 127.0.0.1 9190

得到输出:

Message from server: Hello World!

基于 Windows 的 I/O 函数

Linux中socket也是文件,因此可以通过文件I/O函数read和write进行数据传输。而Windows严格区分文件I/O函数和套接字I/O函数。

int send(SOCKET s,const char* buf,int len,int flags);

成功时返回传输字节数,失败时返回SOCKET_ERROR。

参数:

  • s:表示数据传输对象连接的套接字句柄值
  • buf:保存待传输数据的缓冲地址值
  • len:要传输的字节数
  • flags:传输数据时用到的多种选项信息
int recv(SOCKET s, const char* buf, int len, int flags);

成功时返回接收的字节数(收到EOF时为0),失败返回SOCKET_ERROR。

  • s:表示数据接收对象连接的套接字句柄值
  • buf:保存待接收数据的缓冲地址值
  • len:要接收的字节数
  • flags:接收数据时用到的多种选项信息

习题

(1)套接字在网络编程中的作用是什么?为什么称它为套接字?

网络编程就是编写程序让两台联网的计算机相互交换数据。在我们不需要考虑物理连接的情况下,我们只需要考虑如何编写传输软件。操作系统提供了名为“套接字”,套接字是网络传输传输用的软件设备。

socket英文原意是插座,我们把插头插到插座上就能从电网获得电力供给,同样,为了与远程计算机进行数据传输,需要连接到Internet,而变成中的“套接字”就是用来连接该网络的工具。

(2)在服务器端创建套接字后,会依次调用listen函数和accept函数。请比较并说明两者作用。

listen:将套接字转为可接受连接方式(监听套接字)

accept:受理连接请求,并且在没有连接请求的情况调用该函数,不会返回(阻塞)。直到有连接请求为止。二者存在逻辑上的先后关系。

(3)Linux中,对套接字数据进行I/O时可以直接使用I/O相关函数;而在Windows中则不可以。原因为何?

Linux把套接字也看作是文件,所以可以用文件I/O相关函数;而Windows要区分套接字和文件,所以设置了特殊的函数。

(4)创建套接字后一般会给它分配地址,为什么?为了完成地址分配需要调用哪些函数?

要在网络上区分来自不同机器的套接字,所以需要地址信息。分配地址是通过bind()函数实现。

(5)Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。

Linux的文件描述符是为了区分指定文件而赋予文件的整数值(相当于编号)。Windows的文件描述符其实也是套接字的整数值,其目的也是区分指定套接字。

(6)底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别?

  1. ANSI标准定义的输入、输出函数是与操作系统(内核)无关的以C标准写成的函数;相反,底层文件I/O函数是操作系统直接提供的。
  2. 标准I/O分为全缓冲,行缓冲,不缓冲三种形式;文件I/O为不带缓冲的I/O。
  3. 文件I/O主要针对文件操作,它操作的是文件描述符;标准I/O针对的是控制台,它操作的是字符流。对于不同设备得特性不一样,必须有不同API访问才最高效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/884265.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代理人工智能如何应对现代威胁的速度和数量

Seven AI首席执行官 Lior Div 讨论了代理 AI 的概念及其在网络安全中的应用。他解释了代理 AI 与传统自动化安全系统的区别&#xff0c;即代理 AI 具有更大的自主性和决策能力。 Div 强调&#xff0c;通过实时处理大量警报&#xff0c;代理 AI 特别适合对抗现代 AI 驱动的威胁…

Supabase:当开源遇上实时数据库服务

在当代应用开发的浪潮中,我们见证了无服务器架构和实时数据库服务的崛起。Firebase 的成功验证了这一方向,但同时也带来了供应商锁定的困扰。正是在这样的背景下,Supabase 横空出世,以开源的姿态为开发者提供了一个全新的选择。 为什么 Supabase 值得关注&#xff1f; 当我们…

【华为HCIP实战课程二十九】中间到中间系统协议IS-IS邻居关系建立和LSP详解,网络工程师

一、广播环境邻居关系建立详解 1、广播环境邻居关系建立 广播邻居关系采用三次握手,携带的邻居列表为接口的MAC来标识 2、LSP同步:3种报文(CSNP和PSNP和具体的LSP) CSNP作用类似DBD,请求者发送PSNP(类似LSR)来请求具体的LSP 广播网络LSP交互过程: R1-R2(DIS)--R3…

<HarmonyOS第一课>应用/元服务上架的课后习题

善者&#xff0c;吾善之&#xff1b; 不善者&#xff0c;吾亦善之&#xff0c;德善。 信者&#xff0c;吾信之&#xff1b; 不信者&#xff0c;吾亦信之&#xff0c;德信。 圣人在天下&#xff0c;歙歙焉为天下浑其心&#xff0c;百姓皆注其耳目&#xff0c;圣人皆孩之。 通过&…

游戏引擎中Static,Kinematic,Dynamic三种刚体属性

一.刚体属性 为了提高物理检测效率和内存使用,引擎只对特殊标识的刚体属性进行检测。包括常用的RayCast检测,BoxCast检测,AABB包围盒检测。 Static:实际游戏中不可能发生移动的房子,树木,建筑物等。引擎初始化进行Collider刷新 Kinematic:实际游戏中奇特的物体,传送门,陷阱等, …

【系统架构设计师】预测试卷一:论文(包括4篇论文主题对应的写作要点分析)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 试题一:论面向服务的架构设计与应用试题一写作要点试题二:论软件架构的脆弱性试题二 写作要点试题三:论分布式存储系统架构设计试题三 写作要点试题四:论网络安全体系架构设计及应用试题四 写作要点试题一:论面…

【如何获取股票数据30】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深A股炸板股池数据获取实例演示及接口API说明文档

最近一两年内&#xff0c;股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步&#xff0c;就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息&#xff0c;这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要任…

SpringBoot实现国密通信

SpringBoot实现国密通信 1. 生成支持国密的证书1.1 安装 GMSSL&#xff08;如果未安装&#xff09;1.2 生成 SM2 密钥对和证书 2. 配置 Spring Boot 项目使用 HTTPS2.1 项目 B&#xff1a;提供 HTTPS 接口2.2 项目 A&#xff1a;使用 HTTPS 调用项目 B 3. 启动和测试备注如何验…

九识智能与徐工汽车达成战略合作,共绘商用车未来新蓝图

近日&#xff0c;九识智能与徐工汽车签署战略合作协议&#xff0c;标志着双方在智能驾驶技术与新能源商用车融合应用、联合生产及市场推广等方面迈入深度合作的新篇章&#xff0c;将共同引领智能驾驶技术商业化浪潮。 近年来&#xff0c;在国家智能化发展战略的引领下&#xff…

基于深度学习的社交网络中的社区检测

在社交网络分析中&#xff0c;社区检测是一项核心任务&#xff0c;旨在将网络中的节点&#xff08;用户&#xff09;划分为具有高内部连接密度且相对独立的子群。基于深度学习的社区检测方法&#xff0c;通过捕获复杂的网络结构信息和节点特征&#xff0c;在传统方法基础上实现…

根据Redis漏洞通知的整改修复过程

一、收到通知&#xff1a; 二、查看本校“宝山商城&#xff08;教学&#xff09;”已安装的Redis版本号 对照影响范围的版本号&#xff0c;在其内&#xff0c;所以需要升级Redis版本。 三、升级centos中的Redis版本 在Cent0S系统中&#xff0c;如果我们需要升级Redis版本&…

如何修改远程分支?修改了会影响什么?

要修改 Git 远程仓库中的分支名称&#xff0c;可以按照以下步骤操作&#xff1a; 首先&#xff0c;在本地仓库中重命名分支。使用以下命令将当前分支重命名为新名称&#xff1a; git branch -m <old-branch-name> <new-branch-name>例如&#xff0c;如果你想要将名…

12. 内存管理

一、内存的组织方式 程序员编写完程序之后&#xff0c;程序要先加载在计算机的内存中&#xff0c;再运行程序。在 C 中&#xff0c;不同数据在内存中所存储的位置也不一样。全局变量存储在内存中的静态存储区&#xff0c;非静态的局部变量存储在内存中的动态存储区&#xff08;…

docker 可用镜像服务地址(2024.10.31亲测可用)

1.错误 Error response from daemon: Get “https://registry-1.docker.io/v2/” 原因&#xff1a;镜像服务器地址不可用。 2.可用地址 编辑daemon.json&#xff1a; vi /etc/docker/daemon.json内容修改如下&#xff1a; {"registry-mirrors": ["https://…

Git (推送到远端仓库)

目录 一、在 gitee 上创建一个仓库 二、将项目推送到远程仓库 三、解释推送命令 一、在 gitee 上创建一个仓库 操作如下&#xff1a; 二、将项目推送到远程仓库 这里例举新的项目推送到远程仓库的例子&#xff1a; 打开仓库查看&#xff1a; 三、解释推送命令 添加远程仓库…

2025前端面试-性能优化(HTML与CSS)---003

1.什么是SEO 定义 &#xff1a;Serach Engine Optimization涉及到的使用&#xff1a; 门户网站或者功能网站&#xff08;搜索引擎展示的先后顺序&#xff0c;推广 如搜索pdf转化器搜索之后&#xff0c;同质化显示出来的内容先后会决定网站的点击量&#xff09; 2. SEO有哪些关…

自监督强化学习:对比预测编码(CPC)算法深度解析

自监督强化学习&#xff1a;对比预测编码&#xff08;CPC&#xff09;算法深度解析 引言 在人工智能领域&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&#xff09;与自监督学习&#xff08;Self-Supervised Learning, SSL&#xff09;的结合正在引领一场革命…

opencv学习笔记(6):图像预处理(直方图、图像去噪)

3.直方图 直方图是用来表现图像中亮度分布的&#xff0c;给出的是图像中某个亮度或者某个范围亮度下共有几个像素&#xff0c;即统计一幅图某个亮度像素的数量。 直方图不能反映某一灰度值像素在图像中的位置&#xff0c;失去了图像的空间信息。图像直方图由于其计算代价较小&a…

C# 中 发送HTTP请求 实现Ajax功能

Ajax技术的核心及其在jQuery中的使用&#xff0c;包括GET和POST请求的实现&#xff0c;以及Ajax轮询请求的工作原理。通过实例展示了如何使用jQuery.Ajax进行HTTP请求&#xff0c;与后端接口交互&#xff0c;以及处理JSON数据。同时还提到了跨域问题和轮询请求中停止条件的设定…

使用socket库创建简单的客户端和服务器

以下是使用Python的socket库创建简单的客户端和服务器的示例。这个示例将展示如何建立一个基本的TCP连接&#xff0c;服务器接收数据并发送响应&#xff0c;客户端发送数据并接收响应。 服务器端代码 首先&#xff0c;我们编写服务器端代码&#xff0c;它将监听来自客户端的连…