对于Linux下的服务器编程(2)

对于惊群问题,我们可以使用一个主线程来接受连接,并且把这个连接套接字传递到子进程里面,让子进程来处理这个连接。这种方法需要进程间通信:通过Unix套接字来在进程之间传递套接字。【注意不能使用Unix套接字***直接***传递描述符到子进程,因为虽然父进程和子进程获得的文件描述符相同,但是子进程不一定打开了这个描述符的文件,或者说这两个描述符指向不同的文件,所以必须使用recvmsg/sendmsg这两个函数来传递


#include "ipcunix.h"

extern int sendFD(int fd, int fdSend) {
	struct iovec iov[1];
	struct msghdr msg;
	memset(&msg, 0, sizeof(msg));
	char buf[128];

	const char* str = "Hello World.\n";
	memcpy(buf, str, strlen(str));
	iov[0].iov_base = buf;
	iov[0].iov_len = 128;

	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;

	struct cmsghdr* cmsg = malloc(sizeof(struct cmsghdr));
	if (fdSend < 0) {
		return -1;
	} else {
		memset(cmsg, 0, sizeof(cmsg));
		cmsg->cmsg_level = SOL_SOCKET;
		cmsg->cmsg_type = SCM_RIGHTS;
		cmsg->cmsg_len = CONTROLLEN;

		msg.msg_control = cmsg;
		msg.msg_controllen = CONTROLLEN;
		*(int*)CMSG_DATA(cmsg) = fdSend;
	}

	int numSend;
	if ((numSend = sendmsg(fd, &msg, 0)) < 0) {
		perror("Send Msg Error");
		return -1;
	}
	free(cmsg);
	return 0;
}

extern int recvFD(int fd, ssize_t (*userfunc)(int, const void*, size_t)) {
	int newFD, nr;
	char* ptr;
	char buf[128];
	struct iovec iov[1];
	struct msghdr msg;
	struct cmsghdr* cmsg = malloc(sizeof(struct cmsghdr));
	memset(cmsg, 0, sizeof(struct cmsghdr));

	memset(buf, 0, 128);
	while (1) {
		iov[0].iov_base = buf;
		iov[0].iov_len = 128;
		msg.msg_iov = iov;
		msg.msg_iovlen = 1;
		msg.msg_name = NULL;
		msg.msg_namelen = 0;
		msg.msg_control = cmsg;
		msg.msg_controllen = CONTROLLEN;

		if ((nr = recvmsg(fd, &msg, 0)) < 0) {
			perror("Recv Msg Error");
			return -1;
		} else if (nr == 0) {
			printf("Connection closed by peer.\n");
			return -1;
		} else {
			newFD = *(int*)CMSG_DATA(cmsg);
			return newFD;
		}
	}
}

上面的代码需要注意:

1.cmsghdr结构体需要分配在堆上,如果直接放在栈上,cmsghdr在栈上的地址跟变量声明的顺序不一样[我也不知道为什么]。

2.cmsghdr的type成员在send时候的取值是固定的,必须是SCM_RIGHTS,这样内核就会帮我们把newFD这个文件指针指向父进程打开的连接。


服务器代码里面,需要添加的代码只是对于新连接进来时候的处理:采用轮询的方法来选择不同的子进程来处理连接:

numThread = cpu_num_core - 1;

int clientFD = accept(listenFD, (struct sockaddr*)&cli, &len);

sendFD(processes[(currentProcess++) % numThread].connectFD, clientFD);
close(clientFD);

上面的代码需要注意的是最后的close:因为通过Unix套接字把描述符传递给了子进程,父进程这边的描述符必须关闭,要不然对这个描述符的引用是2,子进程如果关闭这个连接,不是真正的关闭,只是引用-1,可能会出现Bad File Descriptor的错误。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值