简单的回声服务器实现
项目需求
实现回声服务器的客户端/服务器程序,客户端通过网络连接到服务器,并发送任意一串英文信息,服务器端接收信息后,将每个字符转换为大写并回送给客户端显示。
技术分析
1.网络字节序
2.sockaddr数据结构
3.ip地址转换函数
4.socket函数
5.bind函数
6.listen函数
7.accept函数
8.connect函数
9.write函数
10.read函数
网络通信与socket,这一部分博主以前做过详细的分析所以直接附上以前的博客链接了:以下链接均为作者--socket编程专栏文章。
博主相关的技术博客
- 网络编程之 socket编程
网络编程函数小总结与初识socket
网络编程之bind()的未解之谜
网络编程之 字节序和深入理解bind()函数
网络编程之 listen()函数的使用与三次握手的理解
socket编程之 accept函数的理解
socket编程之 connect()函数
基于 Linux 的文件操作 网络编程的最后一环
其实呢,你去看博主的网络编程专栏会发现就这这几天博主其实就写了一个回声服务器而且在那篇博客的基础上列举了一些大家写最基本的回声服务器会遇到的一些错误情况。因为本篇博客也是写回声服务器所以我会基于之前的那两篇博客来发挥,主要也就是增加一部分测试案例。
server.c
#include<stdio.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<arpa/inet.h> #include<stdlib.h> #include<netinet/in.h> #include<ctype.h> #define SERVER_PORT 6666 #define MAXLINE 100 int main(void) { /*定义 server端套接字文件描述符:sfd client套接字文件描述符:cfd read函数读取到的字符数:n */ int sfd, cfd, n; /* server端地址定义(包含IP、PORT、协议族)暂未指定:server_addr client端地址定义(包含IP、PORT、协议族)不需要再server.c定义,accept函数会自动填充*/ struct sockaddr_in server_addr, client_addr; socklen_t client_len;//为 accept函数第三个参数做准备 char buf[MAXLINE];//接收client端发来的字符的缓冲区 /*bzero:将server端置空,为了给后续的IP、PORT、协议族赋值所用 后续操作是为了 bind函数绑定IP、PORT和协议族的固定操作。*/ bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET;//IPV4 server_addr.sin_port = htons(SERVER_PORT);//转换为网络字节序 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); sfd = socket(AF_INET, SOCK_STREAM, 0); //调用 socket函数值后会返回一个文件描述符 bind(sfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); //绑定IP、PORT、协议族 listen(sfd, 21); /* accept函数放在 while 里面和外面的结果是不一样的, accept放在while里面代表客户端只能和服务器端通信一次 accept放在while外面那么客户端就可以一直和服务器进行通信 */ while (1) { client_len = sizeof(client_addr); cfd = accept(sfd, (struct sockaddr*)&client_addr, &client_len);//accept调用和会给server端返回一个和client端连接好的socket。 n = read(cfd, buf, MAXLINE); for (int i = 0; i < n; i++) { buf[i] = toupper(buf[i]); } write(cfd, buf, n); close(cfd); } return 0; }
client.c
#include<stdio.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<arpa/inet.h> #include<stdlib.h> #include<netinet/in.h> #include<ctype.h> #define SERVER_PORT 6666 #define MAXLINE 100 int main(void) { int sfd, n; struct sockaddr_in server_addr; char buf[MAXLINE]; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); sfd = socket(AF_INET, SOCK_STREAM, 0); connect(sfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); while (1) { fgets(buf, sizeof(buf), stdin); write(sfd, buf, strlen(buf)); n = read(sfd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, n); } close(sfd); return 0; }
ssh1:
gcc server.cpp -o server
./server
ssh2:
gcc client.cpp -o client
./client
hello server.c
我们可以看到当启动 server端到结束时都没有任何东西(其实我们可以增加一些输出比如说显示客户端的ip地址或者端口号,甚至还可以将转换好的字符串输出数来,可以在后续的博客进行实现,本博客为了简便甚至没有加上出错处理函数,就为了突出整个程序的逻辑!!)
client端:当我们输入:hello server.c,,马上就能得到服务器返回给我们转成大写的字符串,可是当我们在输入字符串的时候服务器已经不理我们了,甚至还结束了我们的连接(原因是因为我们把close函数放在while循环里面的)。
测试
close放在while外会发生什么呢?
结果依旧是服务器只反应了我们的第一个字符串,又因为没有close函数关闭我们可以一直输入字符串,但是服务器都不会再进行响应了。
增加需求
服务器和客户端通讯建立后服务器能够一直回响客服端发来的消息
技术实现:
将accept函数放在while函数外面,再将close函数放在while函数外面即可。
代码对比:
实现效果:(改变代码之后记得要重新编译源cpp文件)
ssh1:
gcc server.cpp -o server
./server
ssh2:
./client
server端的结果和上面是一样的
感谢观看。