引言
之前都介绍了多线程,多进程的方式解决各种TCP服务器的问题。那么本篇笔记主要记录单进程非阻塞的方式处理TCP服务器的服务。使用的方法就是轮询的机制处理,虽然这种方法很少使用,因为其占用的CPU资源很多,但是还是很有必要了解一下。
各种模型的理解图
- 第一种是前面笔记的多线程TCP服务端之简易QQ/多进程处理TCP服务器IO模型之并发阻塞处理多任务的方式
- 第二种是本篇笔记的主要内容,单进程要处理多个任务,只能通过轮询的方式
- 第三种是UDP专属的信号驱动模式UDP服务器IO模型之信号驱动,这种模式是依靠信号的,TCP任务太多,产生的信号也很多,因此不能使用。
- 第四种是通过使用Poll 函数实现的TCP服务器多路复用的功能处理。
代码实现
server.c:
#include "head4sock.h"
#include "kernel_list.h"
typedef struct
{
int fd ;
struct list_head list;
}client;
client *new_cli(int fd)
{
client *new = malloc(sizeof(client));
if (new != NULL)
{
new->fd =fd ;
INIT_LIST_HEAD(&new->list);
}
return new;
}
client *init_list(void)
{
client *head = malloc(sizeof(client));
if (head != NULL)
{
INIT_LIST_HEAD(&head ->list);
}
return head;
}
int main(int argc, char const *argv[])
{
if(argc != 2)
{
printf("Usage: %s <PORT>\n", argv[0]);
exit(0);
}
// 创建一个TCP套接字
int fd = Socket(AF_INET, SOCK_STREAM, 0); //设为IPv4的套接字
// 绑定地址(IP:PORT)
struct sockaddr_in srvaddr, cliaddr;
socklen_t len = sizeof(srvaddr);
bzero(&srvaddr, len);
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(atoi(argv[1]));
// inet_pton(AF_INET, "192.168.1.166", &srvaddr.sin_addr);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
Bind(fd, (struct sockaddr *)&srvaddr, len);
// 设置监听套接字
Listen(fd, 3); //如果同时有人发出请求,能够同时处理若干个,在Linux中至少同时允许4+3个连接请求
long val =fcntl(fd,F_GETFL); //得到当前的IO状态
val |= O_NONBLOCK;
fcntl(fd,F_SETFL,val); //设置fd为非阻塞状态
client * head =init_list();
int connfd;
while (1)
{
connfd = accept(fd, (struct sockaddr *)&cliaddr, &len);
if (connfd > 0)
{
long val =fcntl(connfd,F_GETFL); //得到当前的IO状态
val |= O_NONBLOCK;
fcntl(connfd,F_SETFL,val); //设置connfd为非阻塞状态
char peeraddr[50];
bzero(peeraddr, 50);
printf("new connection: %s:%hu\n", //打印端口号
inet_ntop(AF_INET, &cliaddr.sin_addr, peeraddr, 50),
ntohs(cliaddr.sin_port));
client * new = new_cli(connfd); //为了更好利用资源,使用动态链表将套接字起来
list_add_tail(&new->list,&head->list); //将新的节点添加到链表中
}
char buf[SIZE];
struct list_head *pos,*n;
list_for_each_safe(pos,n,&head->list)
{
client *tmp = list_entry(pos,client,list);
bzero(buf,SIZE);
int nread;
if((nread=read(tmp->fd,buf,SIZE))==0)
{
list_del(pos);
free(tmp);
}
else if(nread >0) //此时有数据
{
printf("%s",buf );
}
}
}
}
client .c :
与上一篇一样
代码运行结果:
从图中可以看到 server占用的CPU资源比较多,效率比较低,非阻塞模型的轮询机制一般不采用。