Bootstrap

TCP并发服务器

多进程并发服务器

  1. 主进程监听:服务器在指定端口上监听客户端连接请求。
  2. 接受连接:当有新的客户端连接时,服务器接受该连接,并为其派生一个新的子进程。
  3. 进程处理:子进程独立运行,处理客户端的请求,直到客户端断开连接。子进程在处理过程中可以读取数据、进行处理,并返回结果。
  4. 进程终止:子进程处理完毕后终止,释放相关资源。
#include <head.h>

int init_tcp_ser(const char *ip ,unsigned short port)
{	
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail socket");
		return -1;
	}
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(port);
	ser.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
	if (-1 == ret)
	{
		perror("fail bind");
		return -1;
	}

	ret = listen(sockfd, 128);
	if (-1 == ret)
	{
		perror("fail listen");
		return -1;
	}
	
	return sockfd;
}

void handler(int signo)
{
	wait(NULL);
}

int main(int argc, const char *argv[])
{
	pid_t pid = 0;
	int connfd = 0;
	char buff[1024] = {0};

	signal(SIGCHLD, handler);
	int sockfd = init_tcp_ser("192.168.1.100", 50000);
	if (-1 == sockfd)
	{
		return -1;
	}

	while (1)
	{
		connfd = accept(sockfd, NULL, NULL);
		if (-1 == connfd)
		{
			perror("fail accept");
			return -1;
		}
		pid = fork();
		if (pid > 0)
		{
		}
		else if (0 == pid)
		{
			while (1)
			{
				memset(buff, 0, sizeof(buff));
				ssize_t size = recv(connfd, buff, sizeof(buff), 0);
				if (size <= 0)
				{
					break;
				}
				printf("cli---> %s\n", buff);
				strcat(buff, "--->ok!");
				send(connfd, buff, strlen(buff), 0);
			}
			close(connfd);
		}
	}

	return 0;
}

多线程并发服务器

  1. 主线程监听:服务器在指定端口上监听客户端连接请求。
  2. 接受连接:当有新的客户端连接时,服务器接受该连接,并为其创建一个新的线程。
  3. 线程处理:新线程负责处理该客户端的所有请求,直到客户端断开连接。线程可以读取客户端发送的数据、进行处理,并将结果发送回客户端。
  4. 线程终止:在处理完毕后,线程可以选择继续等待新的请求(长连接)或终止(短连接)。
#include <head.h>

int init_tcp_ser(const char *ip ,unsigned short port)
{	
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail socket");
		return -1;
	}
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(port);
	ser.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
	if (-1 == ret)
	{
		perror("fail bind");
		return -1;
	}

	ret = listen(sockfd, 128);
	if (-1 == ret)
	{
		perror("fail listen");
		return -1;
	}
	
	return sockfd;
}

void *do_communicate(void *arg)
{
	int connfd = *((int *)arg);
	char buff[1024] = {0};
	while (1)
	{
		ssize_t size = recv(connfd, buff, sizeof(buff), 0);
		if (size <= 0)
		{
			break;
		}
		printf("cli---> %s\n", buff);
		strcat(buff, "----->ok!");
		send(connfd, buff, strlen(buff), 0);
	}
	close(connfd);

	return NULL;
}

int main(int argc, const char *argv[])
{
	pid_t pid = 0;
	int connfd = 0;
	pthread_t tid;
	char buff[1024] = {0};


	int sockfd = init_tcp_ser("192.168.1.163", 50000);
	if (-1 == sockfd)
	{
		return -1;
	}

	while (1)
	{
		connfd = accept(sockfd, NULL, NULL);
		if (-1 == connfd)
		{
			perror("fail accept");
			return -1;
		}
		pthread_create(&tid, NULL, do_communicate, &connfd);
		pthread_detach(tid);
	}

	return 0;
}

IO模型

1.阻塞IO

#include <head.h>
int main(int argc, char *argv[])
{
    char buf[1024] = {0};
    mkfifo("./myfifo",0664);

    int fifofd = open("./myfifo",O_RDONLY);
    if(-1 == fifofd)
        handle_error("open fail");
    
    while(1)
    {
        fgets(buf,sizeof(buf),stdin);
        printf("stdin: %s\n",buf);

        memset(buf,0,sizeof(buf));
        read(fifofd,buf,sizeof(buf));
        printf("fifo: %s\n",buf);
    }
    close(fifofd);
    return 0;
}

2.非阻塞IO

fcntl

  int fcntl(int fd, int cmd, ... /* arg */ );      

(1)功能:设置文件描述符属性 
(2)参数:

  • fd:文件描述符 
  • cmd:F_GETFL        获得文件描述符属性
  • F_SETFL        设置文件描述符属性 

(3)返回值:

  • F_GETFL:成功返回获得的属性;失败返回-1 
  • F_SETFL:成功返回0;失败返回-1 

O_NONBLOCK   非阻塞;O_ASYNC      异步方式        

#include <head.h>
int main(int argc, char *argv[])
{
    char buf[1024] = {0};
    mkfifo("./myfifo",0664);

    int fifofd = open("./myfifo",O_RDONLY);
    if(-1 == fifofd)
        handle_error("open fail");
    
    //获取文件原有属性
    int flag = fcntl(0,F_GETFL);

    //增加非阻塞属性  
    flag = flag | O_NONBLOCK;
    
    //去掉非阻塞属性
    //flag = flag & (~O_NONBLOCK);

    //设置属性
    fcntl(0,F_SETFL,flag);

    while(1)
    {
        memset(buf,0,sizeof(buf));
        fgets(buf,sizeof(buf),stdin);
        printf("stdin: %s\n",buf);

        memset(buf,0,sizeof(buf));
        read(fifofd,buf,sizeof(buf));
        printf("fifo: %s\n",buf);
    }
    close(fifofd);
    return 0;
}

3.信号驱动IO

#include <head.h>

void handle(int signo)
{
    char buf[1024] = {0};
    fgets(buf,sizeof(buf),stdin);
    printf("stdin: %s\n",buf);
}
int main(int argc, char *argv[])
{
    signal(SIGIO,handle);
    char buf[1024] = {0};
    mkfifo("./myfifo",0664);

    int fifofd = open("./myfifo",O_RDONLY);
    if(-1 == fifofd)
        handle_error("open fail");

    //获取文件原有属性
    int flag = fcntl(0,F_GETFL);
    flag = flag | O_ASYNC;
    fcntl(0,F_SETFL,flag);

    //关联当前进程
    fcntl(0,F_SETOWN,getpid());

    while(1)
    {
        memset(buf,0,sizeof(buf));
        read(fifofd,buf,sizeof(buf));
        printf("fifo: %s\n",buf);
    }
    close(fifofd);
    return 0;
}

4.多路复用IO

1.select

int select(int nfds,  fd_set *readfds,  fd_set *writefds,  fd_set *exceptfds,  struct timeval *timeout);

(1)功能:监听文件描述符集合
(2)参数:

  • nfds:监测的文件描述符上限值(最大文件描述符的值+1)
  • readfds:读文件描述符集合
  • writefds:写文件描述符集合
  • exceptfds:异常条件的描述符集合
  • timeout:设置超时时间;NULL:一直等待            

(3)返回值:

  • 成功返回产生事件文件描述符个数
  • 失败返回-1 
  • 定时时间到达仍没有事件产生返回0 

       void FD_CLR(int fd, fd_set *set);
       将fd从文件描述符集合中清除
       
       int  FD_ISSET(int fd, fd_set *set);
       判断文件描述符fd是否仍在文件描述符集合中
       
       void FD_SET(int fd, fd_set *set);
       将fd加入文件描述符集合中
        
       void FD_ZERO(fd_set *set);
       文件描述符集合清0 

#include <head.h>
int main(int argc, char *argv[])
{
    int maxfd = 0;
    char buf[1024] = {0};
    mkfifo("./myfifo",0664);

    int fifofd = open("./myfifo",O_RDONLY);
    if(-1 == fifofd)
        handle_error("open fail");

    //创建文件描述符集合
    fd_set rdfds;
    fd_set tmfds;

    //清空集合
    FD_ZERO(&rdfds);

    //添加关注的文件描述符到集合中
    FD_SET(0,&rdfds);
    maxfd = 0 > maxfd ? 0 : maxfd;
    FD_SET(fifofd,&rdfds);
    maxfd = fifofd > maxfd ? fifofd : maxfd;

    while(1)
    {
        tmfds = rdfds;

        //开始监测集合中的io
        int cnt = select(maxfd+1, &tmfds, NULL, NULL, NULL);
        if(cnt < 0)
            handle_error("select fail");

        //根据返回值的结果区分处理不同的io
        if(FD_ISSET(0, &tmfds))
        {
            memset(buf, 0, sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            printf("stdin: %s\n",buf);
        }

        if(FD_ISSET(fifofd, &tmfds))
        {
            memset(buf,0,sizeof(buf));
            read(fifofd,buf,sizeof(buf));
            printf("fifo: %s\n",buf);
        }
    }
    close(fifofd);
    return 0;
}

2.poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(1)功能:监听文件描述符集合中的事件
(2)参数:

  • ds:文件描述符集合事件数组首地址
  • nfds:事件个数
  • timeout:超时时间

(3)返回值:

  • 成功返回产生事件的文件描述符个数
  • 失败返回-1 
  • 超时时间到达仍没有产生事件返回0 
       struct pollfd {
       int   fd;         /* file descriptor */
       short events;     /* requested events */
       short revents;    /* returned events */
    };

3.epoll模型

(1)epoll_create

    int epoll_create(int size);

1)功能:创建一个监听事件表(内核中)
2)参数:size:监听事件最大个数
3)返回值:成功返回非负值:表示epoll事件表对象(句柄);失败返回-1         


(2)epoll_ctl

  int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

1)功能:在监听事件表中新增一个事件
2)参数:
        epfd:事件表文件描述符 
        op:         EPOLL_CTL_ADD        新增事件
                      EPOLL_CTL_MOD        修改事件 
                      EPOLL_CTL_DEL        删除事件
         fd:文件描述符 

         event:EPOLLIN  读事件;    EPOLLOUT    写事件

struct epoll_event {
           uint32_t     events;      /* Epoll events */
           epoll_data_t data;        /* User data variable */
        };

3)返回值:成功返回0 ;失败返回-1    

(3)epoll_wait

  int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

1)功能:监听事件表中的事件,并将产生的事件存放到结构体数组中
2)参数:

  • epfd:事件表文件描述符
  • events:存放结果事件结构体数组空间首地址 
  • maxevents:最多存放事件个数
  • timeout:超时时间; -1:阻塞等待直到有事件发生 

3)返回值:

  • 成功返回产生事件个数
  • 失败返回-1 
  • 超时时间到达没有事件发生返回0 
#include <head.h>
#include <sys/epoll.h>

int epoll_add_fd(int epfds, int fd, uint32_t event)
{
    struct epoll_event ev;
    ev.events = event;
    ev.data.fd = fd;
    int ret = epoll_ctl(epfds, EPOLL_CTL_ADD, fd, &ev);
    if(-1 == ret)
        handle_error("epoll_ctl fail");

    return 0;
}
int epoll_del_fd(int epfds, int fd)
{
    int ret = epoll_ctl(epfds, EPOLL_CTL_DEL, fd, NULL);
    if(-1 == ret)
        handle_error("epoll_ctl fail");

    return 0;
}

int main(int argc, char *argv[])
{
    int maxfd = 0;
    char buff[1024] = {0};
    mkfifo("./myfifo",0664);

    int fifofd = open("./myfifo", O_RDONLY);
    if(-1 == fifofd)
        handle_error("open fail");
    
    struct epoll_event evs[2];
    int epfds = epoll_create(2);
    if(-1 == epfds)
        handle_error("epoll_create fail");

    epoll_add_fd(epfds, 0, EPOLLIN);
    epoll_add_fd(epfds, fifofd, EPOLLIN);

    while(1)
    {
        int cnt = epoll_wait(epfds, evs, 2, -1);
        if(cnt < 0)
            handle_error("epoll_wait fail");

        for(int i = 0; i < cnt; i++)
        {
            if(0 == evs[i].data.fd)
            {
                memset(buff, 0, sizeof(buff));
				fgets(buff, sizeof(buff), stdin);
				printf("STDIN: %s\n", buff);
            }
            else if(fifofd == evs[i].data.fd)
            {
                memset(buff, 0, sizeof(buff));
                ssize_t size = read(evs[i].data.fd, buff, sizeof(buff));
                if(size < 0)
                {
                    epoll_del_fd(epfds, evs[i].data.fd);
                    close(evs[i].data.fd);
                    continue;
                }
                printf("FIFO: %s\n", buff);
            }
        }
    }

    return 0;
}

;