前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >I/O 实现:多线程,信号驱动的代码实现

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

原创
作者头像
ge3m0r
发布2024-05-14 23:06:17
790
发布2024-05-14 23:06:17

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: %s\r\n", 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("socket\n");
		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 data\n");
			}

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

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

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • I/O
  • 信号驱动实现
  • 多线程 I/O
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档