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 的实现。