由connect函数使用不当导致的小错误
博客说明:
在008.一个简单的网络服务器开发----回声服务器篇博客中我们实现了一个最为简单的回声服务器,当时我们为了突出程序的逻辑性,从而没有对部分函数进行错误检查那么博主在这里指出一个大家可能会遇到的一个经典错误,而且这个错误还不是太好排查的,那就是忽略了connect函数的返回值从而导致客户端发了消息但是服务器端并没有回应。上代码:
server.c
#include<stdio.h> #include<ctype.h> #include<unistd.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #define SERVER_PORT 9527 int main(void) { int fd, cfd, n; struct sockaddr_in server_addr, client_addr; socklen_t len_c; char clie_IP[BUFSIZ]; char buf[1024]; bzero(&server_addr, 0); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); fd = socket(AF_INET, SOCK_STREAM, 0); bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); listen(fd, 25); len_c = sizeof(client_addr); cfd = accept(fd, (struct sockaddr*)&client_addr, &len_c); while (1) { n = read(cfd, buf, sizeof(buf)); for (int i = 0; i < n; i++) { buf[i] = toupper(buf[i]); } write(cfd, buf, n); write(STDOUT_FILENO, buf, n); } close(cfd); return 0; }
client.c
#include<sys/socket.h> #include<arpa/inet.h> #include<unistd.h> #include<string.h> #include<stdio.h> #include<ctype.h> #define SERVER_PORT 9527 #define SERVER_IP "127.0.0.1" int main(void) { int sfd, n; struct sockaddr_in server_addr; char buf[1024]; sfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&server_addr, 0); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr); sfd =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; }
最终效果:
client:可以看出来,并没有实现我们的小写转大写功能
server:
服务器这端直接什么都没有?可以断定可定是出了什么问题了。那么我么继续去排查。
新server.c:主要用于排错
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <strings.h> #include <string.h> #include <ctype.h> #include <arpa/inet.h> #define SERV_PORT 9527 int main(void) { int sfd, cfd; int len, i; char buf[BUFSIZ], clie_IP[BUFSIZ]; struct sockaddr_in serv_addr, clie_addr; socklen_t clie_addr_len; /*创建一个socket 指定IPv4协议族 TCP协议*/ sfd = socket(AF_INET, SOCK_STREAM, 0); /*初始化一个地址结构 man 7 ip 查看对应信息*/ bzero(&serv_addr, sizeof(serv_addr)); //将整个结构体清零 serv_addr.sin_family = AF_INET; //选择协议族为IPv4 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有IP地址 serv_addr.sin_port = htons(SERV_PORT); //绑定端口号 /*绑定服务器地址结构*/ bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); /*设定链接上限,注意此处不阻塞*/ listen(sfd, 64); //同一时刻允许向服务器发起链接请求的数量 printf("wait for client connect ...\n"); /*获取客户端地址结构大小*/ clie_addr_len = sizeof(clie_addr_len); /*参数1是sfd; 参2传出参数, 参3传入传入参数, 全部是client端的参数*/ cfd = accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len); /*监听客户端链接, 会阻塞*/ //打印出连接成功的客户端的ip和端口号 printf("client IP:%s\tport:%d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), ntohs(clie_addr.sin_port)); while (1) { /*读取客户端发送数据*/ len = read(cfd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, len); /*处理客户端数据*/ for (i = 0; i < len; i++) buf[i] = toupper(buf[i]); /*处理完数据回写给客户端*/ write(cfd, buf, len); } /*关闭链接*/ close(sfd); close(cfd); return 0; }
服务器这端接受的客户端IP为0,也就是根本没有完成和客户端连接,所以客户端得到的是小写也就不足为奇了。
原因剖析:
请大家翻回去看看博主之前写的client.c的代码,write和read可定是没有任何问题的,那么还会有什么地方出问题呢?请仔细思考一下。
在回顾connect函数:
我们再来看看 connet函数吧:
截取部分:
完整博客:
socket编程之 connect()函数
不知道大家是否发现了client.c的问题了?
博主一开始也是着了connect函数的道了,他调用成功之后的返回值居然是0而不是socket描述符。
所以我们可以知道为什么在新编写的server.c中会显示client的ip为0.0.0.0了吧。
就是因为这行代码将本来已经与server连接好的sfd置零了。
删除之后在运行:
client:
server:
所以由此我们需要知道,在做服务器开发的时候我们应该去封装一个专门针对于 bind listen accept connect等函数的出错处理,有了出错处理函数,上述情况的发生是可以大大减小的。
大家能猜到我们第11篇博客的内容了吧,那就是写一个完整的错误处理函数。到时候博主在详细给大家介绍那些功能。