目录
一、实验目的
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
可执行文件。
./blockserver
在第二个终端中,运行blockclient
可执行文件。
./blockclient
接下来,分别在服务端和客户端中输入一些字符串,都能互相接收到信息。
六、实验结果
运行结果如下:
七、实验总结
在本次实验中,我们深入学习了如何在Linux环境下使用流式套接字(TCP套接字)来进行客户端-服务器通信。通过编写一个简单的TCP客户端程序,我们不仅掌握了基础的网络编程知识,还体验了实际动手编程的乐趣和挑战。
在实验过程中,我首先熟悉了流式套接字的基本概念和相关系统调用。通过查阅资料和阅读代码示例,我逐步理解了每个函数的作用及其参数的含义。例如,socket()函数用于创建套接字,connect()函数用于建立连接,write()和read()函数分别用于发送和接收数据。
在编写客户端程序时,遇到的第一个挑战是如何正确设置服务器地址结构体sockaddr_in。通过仔细阅读文档并结合代码示例,我了解到需要设置地址族为AF_INET,IP地址可以使用inet_addr()函数转换,端口号则通过htons()函数转换为网络字节序。
另一个挑战是处理与服务器的通信。在实现循环发送和接收消息时,我学会了使用fgets()来获取用户输入,并通过strncmp()函数判断是否输入了“END”从而决定是否退出循环。同时,通过反复测试和调试,确保程序能够正确地发送和接收数据,并处理可能的错误情况。