由一个论坛帖子, 解决udp 服务器无法返回数据给第一个客户端的问题

原来的代码:
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的值,是一样的)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值