I/O 实现:多线程,信号驱动的代码实现

2024-05-14 23:06:17 浏览数 (4)

I/O

之前介绍了五种 I/O ,具体信息可以看文章,然后就是代码实现了,废话不多数,上代码!

信号驱动实现

信号驱动的大概情况是这样的

信号驱动信号驱动

由于之前 ppt 没有保存,所以直接用了昨天的图。

通过上述过程我们大概知道首先要注册一个回调函数。

注册函数代码如下:

代码语言:c复制
 //注册信号函数
struct sigaction sigio_action;
sigio_action.sa_flags = 0;
sigio_action.sa_handler = do_sigio; //do_sigio 为回调函数
sigaction(SIGIO, &sigio_action, NULL);

上述内容是注册信号回调函数内容。

整体代码比较简单,直接上代码。

代码语言:c复制
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    //注册信号函数
	struct sigaction sigio_action;
	sigio_action.sa_flags = 0;
	sigio_action.sa_handler = do_sigio;
	sigaction(SIGIO, &sigio_action, NULL);

	struct sockaddr_in serv_addr;
	memset(&serv_addr, 0, sizeof(struct sockaddr_in));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(9096);
	serv_addr.sin_addr.s_addr = INADDR_ANY;//服务端

	fcntl(sockfd, D_SETOWN, getpid());  //设置接受 sigio 的进程

	int flags = fcntl(sockfd, F_GETFL, 0); //获取进程状态
	flags |= O_ASYNC | O_NONBLOCK; //改变进程标志然后重新设置

	fcntl(sockfd, F_SETFL, flags);

	bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	while(1) sleep(1);

	close(sockfd);

上述较为简单,创建 socket, 注册信号函数,设置 I/O 为非阻塞,绑定端口,然后循环,后边是接受和发送数据,这些是定义在 do_sigio 的回调函数中,当然其中也可以定义业务逻辑,回调函数代码如下:

代码语言:c复制
void do_sigio(int sig){
	struct sockaddr_in cli_addr;
	int clilen = sizeof(struct sockaddr_in);
	int clifd = 0;

    char buffer[256] = {0};
    int len = recvfrom(sockfd, buffer, 256, 0, (struct sockaddr*)&cli_addr, (socklen_t*)clilen);
	printf("listen message: %srn", buffer);

	int slen = sendto(sockfd, buffer, len, 0, (struct sockaddr*)&cli_addr, clilen);
}

上述内容较为简单就是接受数据然后发送。其中回调函数调用依靠内核发送信号后调用。

信号驱动逻辑较为简单。

多线程 I/O

多线程 I/O 就是一个主线程专门负责接受,每接受到一个连接后,然后创建一个线程,将后续接受数据发送数据任务交给创建的线程。

首先就是普通的创建 socket ,然后接受连接过程。

代码语言:c复制
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0){
		perror("socketn");
		return -1;
	}

	struct sockaddr_in addr;
	memset(&addr , 0 , sizeof(struct sockaddr));
	
    addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){
		 perror("bind");
		 return 2;
	}

	if(listen(sockfd, 5) < 0){
		perror("listen");
		return 3;
	}

上述代码较为简单,基本涉及网络都会写到,上述监听了队列为 5 ,一个简单的作用。

然后就是每接受一个连接,然后创建新的线程,然后负责接受发送。

代码语言:c复制
while(1){ 
	   	struct sockaddr_in client_addr;
		memset(&client_addr, 0, sizeof(struct sockaddr_in));

		socklen_t client_len = sizeof(client_addr);
		int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
		if(clientfd < 0) continue;

		pthread_t thread_id;
		int ret = pthread_create(&thread_id, NULL, client_callback, &clientfd); //接受创建
		if(ret < 0){
			perror("pthread_create");
			exit(1);
		}
}

代码逻辑较为简单,接受一个连接后,创建一个相关线程接收 socket。

其中回调函数就是后续的作用内容。

代码语言:c复制
void* client_callback(void *arg)
{
    int clientfd = *(int *)arg;
	while(1){
		char buffer[BUFFER_LENGTH] = {0};
		int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);

		if(ret < 0){
		    if(errno == EAGAIN || errno == EWOULDBLOCK){
				printf("read all datan");
			}

			close(clientfd);
			return NULL;
		}else if(ret == 0){
			printf("disconnectn");
			close(clintfd);
			return NULL;
		}else{
			printf("recv:%s, %d bytes", buffer, ret);
			//return ;
		}
	}
}

线程中回调函数很简单,就是接受数据打印。

这两个实现就到这里,下一篇一起讲 select,poll, epoll 的实现。

1 人点赞