一、实验目的
1、深入理解 TCP/IP 模型相关概念; 2、掌握流式套接字 socket 相关的数据结构,如 sokkaddr,sockaddr_in 等; 3、掌握流式套接字通讯时常用的函数,如 socket,connect,listen,accept 等。
二、实验内容
在网络环境中使用流式套接字完成以下内容:客户端进程 A 向服务器进程 B 发送一个字符,B进程将接收到 A 进程的字符,反之亦然。
实验的主要内容是编写一个TCP客户端程序,该程序能够连接到指定的服务器地址和端口,发送用户输入的消息,并接收服务器的回复。在这个过程中,我们需要完成以下关键步骤:
- 创建套接字:利用socket()函数创建一个TCP套接字。
- 设置服务器地址:配置服务器的IP地址和端口号。
- 连接服务器:使用connect()函数连接到服务器。
- 数据交互:通过write()和read()函数实现与服务器的数据交换。
- 关闭套接字:在通信结束后,关闭套接字释放资源。
三、实验环境
虚拟机软件:VMware 16 Pro Linux操作系统版本:CentOS-7-64位
四、参考代码
blockserver.c - #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #define BUFSIZE 1024
- #define PORT 5058
- int main(int argc, char *argv[]){
- int listen_fd, client_fd;
- struct sockaddr_in server_addr,client_addr;
- ssize_t server_len, client_len ;
- char buffer[BUFSIZE];
- int real_read = -1;
- memset(buffer, BUFSIZE, 0);
- // create socket, socket just like file descriptor
- listen_fd = socket(PF_INET, SOCK_STREAM, 0);
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(PORT);
- server_addr.sin_addr.s_addr = INADDR_ANY;
- server_len = sizeof(struct sockaddr_in);
- // bind listen_fd and server socket address
- bind(listen_fd, (struct sockaddr *) &server_addr, server_len);
- // set listen queue
- listen(listen_fd, 5);
- // wait for client to connect
- // note: accept's third argment is client socket address's pointer
- client_len = sizeof(struct sockaddr_in);
- client_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_len);
- while(1){
- real_read = read(client_fd, buffer, BUFSIZE);
- printf("FROM CLIENT: %s", buffer);
- if( strncmp(buffer,"END",3) == 0)
- break;
- fgets(buffer, BUFSIZE,stdin);
- write(client_fd, buffer,real_read); // echo
- memset(buffer,BUFSIZE, 0);
- }
- close(client_fd);
- close(listen_fd);
- return 0;
- }
复制代码
blockclient.c - #include <stdio.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #define BUFSIZE 1024
- #define PORT 5058
-
- int main(int argc, char* argv[]){
- int server_fd;
- struct sockaddr_in server_addr;
- char buffer[BUFSIZE];
- memset(buffer, BUFSIZE, 0);
- server_addr.sin_family = AF_INET;
- // string -> struct in_addr
- server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- server_addr.sin_port = htons(PORT);
-
- // create client socket
- server_fd = socket(PF_INET, SOCK_STREAM, 0);
- // connect server
- connect(server_fd,(struct sockaddr *) &server_addr, sizeof(server_addr));
- while(1){
- fgets(buffer, BUFSIZE,stdin);
- write(server_fd, buffer, BUFSIZE);
- if(strncmp(buffer,"END",3)==0)
- break;
- memset(buffer, BUFSIZE, 0);
- read(server_fd,buffer,BUFSIZE);
- printf("From Server: %s",buffer);
- }
-
- close(server_fd);
- return 0;
- }
复制代码
五、实验步骤
步骤1. 编辑源代码blockserver.c和blockclient.c
源代码blockserver.c和blockclient.c内容见上述参考代码。 - mkdir test8
- cd test8
- vim blockserver.c
- vim blockclient.c
复制代码
源代码blockserver.c:
首先,定义了缓冲区大小BUFSIZE和服务器监听的端口号PORT。定义了监听套接字listen_fd和客户端套接字client_fd,以及服务器地址server_addr和客户端地址client_addr结构体。buffer数组用于存储接收和发送的数据,real_read用于记录实际读取的字节数。 接着,使用memset函数将buffer数组的所有元素初始化为0。创建一个 TCP 套接字,并将其文件描述符存储在listen_fd中。设置服务器地址结构体server_addr的成员,包括地址族为 IPv4 (AF_INET),端口号为PORT,IP 地址为任意可用地址 (INADDR_ANY)。将listen_fd套接字与指定的server_addr绑定,使服务器能够接收客户端连接请求。 然后,开始监听传入的连接请求,允许最多5个等待连接的队列。接受客户端的连接请求,并创建一个新的套接字client_fd用于与客户端通信。此函数会阻塞程序直到有客户端连接进来。处理通信,在一个无限循环中,服务器从client_fd套接字读取数据到buffer中。如果接收到的数据是 “END”,则退出循环。然后从标准输入 (stdin) 获取数据并将其写回client_fd套接字,实现了简单的回显功能。 最后,在程序结束时关闭client_fd和listen_fd套接字,释放资源。
源代码blockclient.c:
首先,定义了缓冲区大小BUFSIZE和服务器监听的端口号PORT。声明了服务器套接字server_fd、服务器地址结构体server_addr和用于存储消息的缓冲区buffer。使用memset将buffer初始化为0。 接着设置了服务器地址结构体server_addr的成员:地址族为 IPv4 (AF_INET),服务器 IP 地址为本地回环地址127.0.0.1,端口为PORT。创建了 TCP 套接字server_fd,并使用connect函数连接到指定的服务器地址和端口。 然后,在一个无限循环中,程序通过fgets获取用户输入的消息存入buffer,然后使用write函数将消息发送给服务器。如果用户输入了 “END”,则退出循环。接着使用read函数读取服务器的响应,将响应打印到控制台。 最后,在程序结束时关闭客户端套接字server_fd,释放资源。
步骤2. 编译源代码blockserver.c和blockclient.c - gcc blockserver.c -o blockserver -g
- gcc blockclient.c -o blockclient -g
复制代码
步骤3. 运行可执行程序blockserver和blockclient
在第一个终端中,运行blockserver可执行文件。
在第二个终端中,运行blockclient可执行文件。
接下来,分别在服务端和客户端中输入一些字符串,都能互相接收到信息。
六、实验结果
运行结果如下:
七、实验总结
在本次实验中,我们深入学习了如何在Linux环境下使用流式套接字(TCP套接字)来进行客户端-服务器通信。通过编写一个简单的TCP客户端程序,我们不仅掌握了基础的网络编程知识,还体验了实际动手编程的乐趣和挑战。 在实验过程中,我首先熟悉了流式套接字的基本概念和相关系统调用。通过查阅资料和阅读代码示例,我逐步理解了每个函数的作用及其参数的含义。例如,socket()函数用于创建套接字,connect()函数用于建立连接,write()和read()函数分别用于发送和接收数据。 在编写客户端程序时,遇到的第一个挑战是如何正确设置服务器地址结构体sockaddr_in。通过仔细阅读文档并结合代码示例,我了解到需要设置地址族为AF_INET,IP地址可以使用inet_addr()函数转换,端口号则通过htons()函数转换为网络字节序。 另一个挑战是处理与服务器的通信。在实现循环发送和接收消息时,我学会了使用fgets()来获取用户输入,并通过strncmp()函数判断是否输入了“END”从而决定是否退出循环。同时,通过反复测试和调试,确保程序能够正确地发送和接收数据,并处理可能的错误情况。 |