原来的代码:
udp服务器:
可以看到有一个客户程序没有结束(客户端./sockclient2.exe E),但是服务确实已经发送数据了,因为服务器在发送数据后,会输出发送的数据。
udp服务器:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main()
{
int server_sockfd = -1;
int server_len = 0;
int client_len = 0;
char buffer[512];
int result = 0;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(9739);
server_len = sizeof(server_addr);
bind(server_sockfd, (struct sockaddr*)&server_addr, server_len);
signal(SIGCHLD, SIG_IGN);
while(1)
{
result = recvfrom(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len);
if(fork() == 0)
{
buffer[0] += 'a' - 'A';
sleep(5);
sendto(server_sockfd, buffer, sizeof(buffer),0 , (struct sockaddr*)&client_addr, client_len);
printf("%c\n", buffer[0]);
}
}
close(server_sockfd);
}
客户端的代码:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
int main(int agrc, char *argv[])
{
struct sockaddr_in server_addr;
int server_len = 0;
int sockfd = -1;
int result = 0;
char c = 'A';
if(agrc > 1)
c = argv[1][0];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(9739);
server_len = sizeof(server_addr);
sendto(sockfd, &c, sizeof(char), 0,
(struct sockaddr*)&server_addr, server_len);
recvfrom(sockfd, &c, sizeof(char), 0, 0, 0);
printf("char from server = %c\n", c);
close(sockfd);
exit(0);
}
第一次运行结果如下:
可以看到有一个客户程序没有结束(客户端./sockclient2.exe E),但是服务确实已经发送数据了,因为服务器在发送数据后,会输出发送的数据。
///分割线
首先看到帖子的时候,我首先怀疑是fork()出来的子进程没有关闭,处理第二个,第三个客户端的连接出问题导致的, 后来测试,发现不是如此:
为了测试,服务器的代码增加了调试日志:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <arpa/inet.h>
#include <iostream>
#include <error.h>
int main()
{
int server_sockfd = -1;
int server_len = 0;
int client_len = 0;
char buffer[512];
int result = 0;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(9739);
server_len = sizeof(server_addr);
bind(server_sockfd, (struct sockaddr*)&server_addr, server_len);
signal(SIGCHLD, SIG_IGN);
while(1)
{
result = recvfrom(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, (socklen_t *)&client_len);
std::cout << "pid " << getpid() << " currenttime" << time(NULL) << " clientsocket" << client_addr.sin_port <<
" result:" << result << " error" << error << " addrlen" << client_len << std::endl;
if(fork() == 0)
{
buffer[0] += 'a' - 'A';
sleep(5);
std::cout << "newpid" << getpid() << " sendmsg " << (int)buffer[0] << " currenttime:" << time(NULL) <<
" clientsocket" << client_addr.sin_port << " addrlen" << client_len << std::endl;
sendto(server_sockfd, buffer, sizeof(buffer),0 , (struct sockaddr*)&client_addr, client_len);
//printf("%c\n", buffer[0]);
exit(0);
}
}
close(server_sockfd);
}
客户端运行命令:./client.out a & ./client.out b & ./client.out c & 运行结果如下:
可以看到, 第一次客户端的连接,获取到的客户端端口为0, 所以发送回客户端的时候,找不到正确的客户端地址,当然客户端没有收到数据,自然不会退出(退出的不一定是 ./)
再仔细看下代码和控制台输出, 我们就能发现原来下面这行代码导致的问题:
int client_len = 0
这个参数初始化为0, 客户端第一次输出后, client_len的值置为16了
recvfrom这个函数,第六个参数,需要传送的客户端地址信息的长度, 如果传入的是0, 成功接收数据后,会设置为客户端地址信息的长度, 但是对recvfrom函数的第五个参数,不会设置客户端地址信息(,原来如此啊)
到此,解决办法如下, 修改client_len的初始值:
int client_len = sizeof(sockaddr_in);
测试结果如下:
到此问题得到解决
/分割线//
另外我们来做个有趣的测试,如果子进程没有关闭,多个进程同时执行一下代码, 执行recvfrom的进程是那个,是主线程,还是fork出来的子进程,猜猜看,
result = recvfrom(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, (socklen_t *)&client_len);
运行结果如下:
可以看到,第二次执行,既有父进程id:7004, 也有第一次执行 fork 出来的子进程ID:7008,7009
第三次执行,既有父进程, 也有第一次执行fork出来子进程,但是没有第二次执行fork出来子进程(跟操作系统调度有关,咱们今天就不深究了, top看了一下nice的值,是一样的)