代码敲了这么多年,头疼的不是写代码,而是调试代码。自从进了培训行业,我又发现,最头疼的还不是自己调试代码,而是帮别人调试代码。经常有学生直接把整个工程打包发过来:老师,某某某个功能有问题,帮我看下...最常见的问题有:
第一次运行可以,怎么第二次就不行了?
代码昨晚还是好好的,怎么今天就不行了?
我已经保存数据了呀,怎么还是查不到?
客户端发送数据了呀,服务器怎么收不到呢?
我就照着你的代码写的啊,怎么运行结果不对呢?
这些问题,很让人奔溃!不去调试代码,怎么知道呢?下面就来总结总结,我调试过的关于“
客户端发送成功,服务器却收不到”的问题原因。
1 发送的字节数错误
client.c
#include #include /* See NOTES */#include #include #include #include #include #include #include int main(){ int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) { perror("socket"); exit(1); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = 8000; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //向服务器发起连接 int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (-1 == ret) { perror("connect"); exit(1); } char *buf = "helloworld"; //每隔 1S 向服务器发送一个字符串 while (1) { ret = send(sockfd, buf, sizeof(buf), 0); //问题代码 if (-1 == ret) { perror("send"); exit(1); } sleep(1); } close(sockfd); return 0;}
注:注意 37 行,sizeof(buf)
server.c
#include #include /* See NOTES */#include #include #include #include #include #include #include int main(){ //创建socket int sockfd = socket(AF_INET, SOCK_STREAM, 0); //ipv4协议 流式套接字 具体的协议类型 if (-1 == sockfd) { perror("socket"); exit(1); } int opt = 1; setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //地址可以被重复绑定 struct sockaddr_in server_addr; //保存服务器的信息 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = 8000; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //127.0.0.1回环ip 表示本机 测试时候可以用 //绑定信息 int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (-1 == ret) { perror("bind"); exit(1); } //设置监听队列 ret = listen(sockfd, 10); if (-1 == ret) { perror("listen"); exit(1); } printf("等待客户端的连接...\n"); struct sockaddr_in client_addr; //用于保存客户端的信息 int length = sizeof(client_addr); //接受连接(建立TCP连接) int fd = accept(sockfd, (struct sockaddr *)&client_addr, &length); if (-1 == fd) { perror("accept"); exit(1); } printf("接受客户端的连接 %d\n", fd); char buf[32] = {0}; while (1) { //从fd接收消息,TCP连接相当于一个文件,fd就是文件描述符,从fd读取数据,就是从TCP连接接收数据 ret = recv(fd, buf, sizeof(buf), 0); if (-1 == ret) { perror("recv"); exit(1); } printf("%s\n", buf); memset(buf, 0, sizeof(buf)); } close(fd); close(sockfd); return 0;}
运行代码,服务器端看到的现象是这样的:
客户端发送的是字符串 “helloworld”,服务器只收到了部分数据。这种情况,要么是客户端发送字节数错误,要么是服务器接收的字节数错误。最常见的就是代码中的写法。客户端代码,buf是一个指针,用 send 发送数据的时候,习惯性的写成了:
sizeof(buf) //只有四个字节
所以发送的时候只把 “helloworld” 部分字节发送过去。
解决方法:
把客户端的发送的长度改成:
strlen(buf)
2 发送数据和接收数据的格式不一致
client.c
struct Message{ char sex; int id; char name[32];};typedef struct Message Message;int main(){ // .... Message mesg; //每隔 1S 向服务器发送一个字符串 while (1) { memset(&mesg, 0, sizeof(mesg)); mesg.sex = 'm'; mesg.id = 1; strcpy(mesg.name, "jack"); ret = send(sockfd, &mesg, sizeof(mesg), 0); if (-1 == ret) { perror("send"); exit(1); } sleep(1); } // ...}
注:注意结构体的成员。
server.c
struct Message{ int id; char name[32];};typedef struct Message Message;int main(){ // ... Message mesg; while (1) { ret = recv(fd, &mesg, sizeof(mesg), 0); if (-1 == ret) { perror("recv"); exit(1); } printf("id = %d name = %s\n", mesg.id, mesg.name); memset(&mesg, 0, sizeof(mesg)); } // .... return 0;}
服务器端运行结果:
很显然,服务器端收到的数据和客户端发送的数据不一致。因为客户端发送的结构体有三个成员,服务器接收数据的结构体只有两个成员,服务器收到字节流直接填入结构体,根本对应不上。
解决方法:
客户端和服务器结构体保持一致,或者使用相同的头文件。
3 发送的结构体包含指针
client.c
struct Message { char sex; int id; char *name; //指针 不是数组};typedef struct Message Message;int main(){ // .... Message mesg; mesg.sex = 'm'; mesg.id = 1; mesg.name = (char *)malloc(sizeof(char) * 32); strcpy(mesg.name, "jack"); //每隔 1S 向服务器发送一个字符串 while (1) { ret = send(sockfd, &mesg, sizeof(mesg), 0); if (-1 == ret) { perror("send"); exit(1); } sleep(1); }}
注:结构体成员有一个指针。
server.c
struct Message{ char sex; int id; char *name;};typedef struct Message Message;int main(){ Message mesg; while (1) { ret = recv(fd, &mesg, sizeof(mesg), 0); if (-1 == ret) { perror("recv"); exit(1); } printf("sex = %c id = %d name = %s\n", mesg.sex, mesg.id, mesg.name); memset(&mesg, 0, sizeof(mesg)); }}
服务器运行结果:
服务器端直接段错误死掉了。
客户端发送的结构体有一个成员是指针,指向客户端进程的堆空间。客户端在发送数据的时候,直接把指针(即地址)发送过去,而指针指向的内容(即 jack)并没有发送过去。服务器收到的指针,在客户端进程里面是合法的指针,但是在服务器这就变成了野指针。
解决方法:
结构体里面的指针改成数组。
4 一次发送对应多个接收
client.c
int main(){ // .... char *buf = "helloworld"; //每隔 1S 向服务器发送一个字符串 while (1) { ret = send(sockfd, buf, strlen(buf), 0); if (-1 == ret) { perror("send"); exit(1); } sleep(1); }}
server.c
void *receive(void *arg){ int fd = *(int *)arg; int ret; char buf[32] = {0}; while (1) { ret = recv(fd, buf, sizeof(buf), 0); if (-1 == ret) { perror("recv"); } printf("收到客户端的消息:%s\n", buf); memset(buf, 0, sizeof(buf)); } }int main(){ // .... pthread_t tid; ret = pthread_create(&tid, NULL, receive, &fd); if (-1 == ret) { perror("pthread_create"); exit(1); } char buf[32] = {0}; while (1) { ret = recv(fd, buf, sizeof(buf), 0); if (-1 == ret) { perror("recv"); exit(1); } } // ....}
注:主线程和子线程都有接收的函数。
运行结果,服务器收不到任何数据。原因也很简单,只不过把它放在工程里面可能不容易发现。服务器两个线程都在接收数据,所以不好判断哪个 recv 收到了数据。
解决办法:
确保服务器端同一时刻只有一个 recv 函数在接收数据。
5 接收数据存放位置错误
client.c
int main(){ char *buf = "helloworld"; //每隔 1S 向服务器发送一个字符串 while (1) { ret = send(sockfd, buf, strlen(buf), 0); if (-1 == ret) { perror("send"); exit(1); } sleep(1); }}
server.c
int main(){ // .... char *buf = (char *)malloc(sizeof(char) * 128); while (1) { ret = recv(fd, &buf, 128, 0); if (-1 == ret) { perror("recv"); exit(1); } printf("%s\n", buf); }}
注:代码第 8 行,对指针 buf 再取地址。
服务器运行结果:
这个还是指针的使用问题。buf本来就是一个指针指向堆内存,程序的本意是把接收到的数据放在 buf 指向的内存空间,因为多了一个 & 符号,所以进程把收到的数据放在了指针 buf 四个字节中,很显然,一共接收 128 字节,越界访问。
解决办法:
recv 函数去掉 & 符号。以上五种情况是我见过最多的,其实问题一点也不难,主要是放在项目中很难被发现。还有哪些常见错误,欢迎公众号后台回复补充。
PS.如果你还在为简历上没有合适的项目而发愁,如果你还不知道毕业设计该做什么,智能家居项目实战奉上!从零开始,3000行代码体验嵌入式开发完整流程。