目录

TCP并发服务器

TCP并发服务器

单循环服务器 :服务器在同一时刻只能响应一个客户端的需求。

并发服务器 :服务器在同一时刻可以响应多个客户端的需求。

构建TCP服务器的方法:

https://i-blog.csdnimg.cn/direct/f86893ca280e43e79c3724104ab9d161.png

IO多路复用的函数接口 [select()  poll() epoll()]

1.多进程实现TCP并发服务器

#include <stdio.h>
#include "head.h"

#define LISTEN_CLI__MAX_CNT 1024

int init_tcp_ser(const char *ip, unsigned short port)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		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 (ret < 0)
	{
		perror("fail bind");
		return -1;
	}
	
	ret = listen(sockfd, LISTEN_CLI__MAX_CNT);
	if (ret < 0)
	{
		perror("fail listen");
		return -1;
	}

	return sockfd;
}

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

int main(int argc, const char *argv[])
{
	struct sockaddr_in cli;
	
	socklen_t clilen = sizeof(cli);
	
	signal(SIGCHLD, handler);
	int sockfd = init_tcp_ser("192.168.1.179", 50000);
	if (sockfd < 0)
	{
		return -1;
	}

	while (1)
	{
	
		int connfd = accept(sockfd, (struct sockaddr *)&cli, &clilen);
		if (connfd < 0)
		{
			perror("fail accept");
			return -1;
		}
		pid_t pid = fork();
		if (pid > 0)
		{
		}
		else if (0 == pid)
		{
			char buff[1024] = {0};
			while (1)
			{
				memset(buff, 0, sizeof(buff));
				size_t size = recv(connfd, buff, sizeof(buff), 0);
				if (size < 0)
				{
					perror("fail recv");
					exit(1);
				}
				else if (0 == size) // closed
				{
					break;
				}
				printf("[%s : %d][%ld] %s\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port), size, buff);
				strcat(buff, "--->ok");
				size = send(connfd, buff, strlen(buff), 0);
				if (size < 0)
				{
					perror("fail send");
					exit(1);
				}
			}
			exit(0);
		}
		else
		{
			perror("fail fork");
			return -1;
		}
	}

	return 0;
}

2.多线程实现TCP并发服务器

#include <stdio.h>
#include "head.h"

#define LISTEN_CLI__MAX_CNT 1024

int init_tcp_ser(const char *ip, unsigned short port)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		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 (ret < 0)
	{
		perror("fail bind");
		return -1;
	}
	
	ret = listen(sockfd, LISTEN_CLI__MAX_CNT);
	if (ret < 0)
	{
		perror("fail listen");
		return -1;
	}

	return sockfd;
}


void *do_communicate(void *arg)
{
	char buff[1024] = {0};
	int connfd = *(int *)arg;
	while (1)
	{
		memset(buff, 0, sizeof(buff));
		size_t size = recv(connfd, buff, sizeof(buff), 0);
		if (size < 0)
		{
			perror("fail recv");
			return NULL;
		}
		else if (0 == size)
		{
			break;
		}
		printf("%s\n", buff);
		strcat(buff, "---->ok");
		size = send(connfd, buff, strlen(buff), 0);
		if (size < 0)
		{
			perror("fail send");
			return NULL;
		}
	}

	return NULL;
}


int main(int argc, const char *argv[])
{
	struct sockaddr_in cli;
	pthread_t tid;

	socklen_t clilen = sizeof(cli);
	
	int sockfd = init_tcp_ser("192.168.1.179", 50000);
	if (sockfd < 0)
	{
		return -1;
	}

	while (1)
	{
	
		int connfd = accept(sockfd, (struct sockaddr *)&cli, &clilen);
		if (connfd < 0)
		{
			perror("fail accept");
			return -1;
		}
		
		pthread_create(&tid, NULL, do_communicate, &connfd);
		pthread_detach(tid);
	}

	return 0;
}

3.IO多路复用

https://i-blog.csdnimg.cn/direct/d7371a379a494408bccff2d4830a0f3e.png

https://i-blog.csdnimg.cn/direct/b8586cb2a4694989aa95ae9ccebc50a1.png

方法:

select(),poll(),epoll()函数接口的优缺点

https://i-blog.csdnimg.cn/direct/dad62adaf20e4c15b244a5c63019f4a0.png

函数接口: select()

https://i-blog.csdnimg.cn/direct/49e6d020b9914b24a42cd710a6800bac.png


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

具体代码实现 :用select实现数据的接受(并发实现从管道读数据和从终端读数据)

#include <stdio.h>
#include "head.h"

#define LISTEN_CLI__MAX_CNT 1024

int init_tcp_ser(const char *ip, unsigned short port)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		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 (ret < 0)
	{
		perror("fail bind");
		return -1;
	}
	
	ret = listen(sockfd, LISTEN_CLI__MAX_CNT);
	if (ret < 0)
	{
		perror("fail listen");
		return -1;
	}

	return sockfd;
}



int main(int argc, const char *argv[])
{
	struct sockaddr_in cli;
	pthread_t tid;
	int maxfd = -1;
	char buff[1024] = {0};

	socklen_t clilen = sizeof(cli);
	
	int sockfd = init_tcp_ser("192.168.1.158", 50000);
	if (sockfd < 0)
	{
		return -1;
	}
	
	fd_set tmpfds;
	fd_set rdfds;
	FD_ZERO(&rdfds);

	FD_SET(sockfd, &rdfds);
	maxfd = maxfd > sockfd ? maxfd : sockfd;

	
	while (1)
	{
		tmpfds = rdfds;
		int cnt = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
		if (cnt < 0)
		{
			perror("fail select");
			return -1;
		}

		for (int i = 0; i <= maxfd; i++)
		{
			if (FD_ISSET(i, &tmpfds))
			{
				if (i == sockfd)
				{
					int connfd = accept(sockfd, (struct sockaddr *)&cli, &clilen);
					if (connfd < 0)
					{
						perror("fail accept");
						continue;
					}
					FD_SET(connfd, &rdfds);
					maxfd = maxfd > connfd ? maxfd : connfd;

					printf("[%s : %d] online\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port));
				}
				else
				{
					//connfd
					memset(buff, 0, sizeof(buff));
					ssize_t size = recv(i, buff, sizeof(buff), 0);
					if (size < 0)
					{
						perror("fail recv");
						FD_CLR(i, &rdfds);
						close(i);
						continue;
					}
					else if (0 == size)
					{
						FD_CLR(i, &rdfds);
						close(i);
						continue;	
					}
					printf("%s\n", buff);
					strcat(buff, "----ok");
					size = send(i, buff, strlen(buff), 0);
					if (size < 0)
					{
						perror("fail send");
						FD_CLR(i, &rdfds);
						close(i);
						continue;
						
					}
				
				}
			}
		
		}

	
	}

	return 0;
}

select()函数接口的不足处:

https://i-blog.csdnimg.cn/direct/1dea7d8a0d924d0f83154f5912029814.png

函数接口:poll();

poll

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

功能:

监听文件描述符集合中的事件

参数:

fds :文件描述符集合事件数组首地址

nfds :事件个数

timeout :超时时间

返回值:

成功返回产生事件的文件描述符个数

失败返回-1

超时时间到达仍没有产生事件返回0

结构体:

struct pollfd {

int   fd;         /* file descriptor */

short events;     /* requested events */

short revents;    /* returned events */

};

函数接口epoll();

epoll模型

epoll模型:

1 )epoll_create 创建epoll文件描述符集合

2) epoll_ctl 添加关注的文件描述符

3) epoll_wait 监控io事件

4) epoll_ctl 从事件集合中删除完成的文件描述符

epoll_create()

epoll_create

int epoll_create (int size);

功能:

创建一个监听事件表(内核中)

参数:

size: 监听事件最大个数

返回值:

成功 返回非负值:表示epoll事件表对象(句柄)

失败 返回-1

epoll_ctl()

epoll_ctl

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

功能:

在监听事件表中新增一个事件

参数:

epfd: 事件表文件描述符

op

EPOLL_CTL_ADD 新增事件

EPOLL_CTL_MOD 修改事件

EPOLL_CTL_DEL 删除事件

fd :文件描述符

events: 事件相关结构体

返回值:

成功返回0

失败返回-1

结构体说明

typedef union epoll_data {

void        *ptr;

int          fd;

uint32_t     u32;

uint64_t     u64;

} epoll_data_t;

struct epoll_event {

uint32_t events ;      /* Epoll events */

epoll_data_t data ;        /* User data variable */

};

events:

EPOLLIN        读事件

EPOLLOUT    写事件

EPOLLET        边沿触发

LT                    水平触发

用epoll()实现并发的从终端与管道文件中读取数据

#include "../head.h"

#define EPOLL_FD_MAX 2

int epoll_fd_add(int epfds, int fd, uint32_t events)
{
	struct epoll_event ev;
	ev.events = events;
	ev.data.fd = fd;
	int ret = epoll_ctl(epfds, EPOLL_CTL_ADD, fd, &ev);
	if (ret < 0)
	{
		perror("fail epoll_ctl add");
		return -1;
	}
	
	return 0;
}

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

	//保存epoll_wait返回的到达事件的结果
	struct epoll_event evs[EPOLL_FD_MAX];

	mkfifo("./myfifo", 0664);
	
	int fifofd = open("./myfifo", O_RDONLY);
	if (fifofd < 0)
	{
		perror("fail open fifo");
		return -1;
	}
	
	//1 创建文件描述符集合
	int epfds = epoll_create(EPOLL_FD_MAX);
	if (epfds < 0)
	{
		perror("fail epoll_create");
		return -1;
	}
	
	//2 添加关注的文件描述符
	
	epoll_fd_add(epfds, 0, EPOLLIN);
	epoll_fd_add(epfds, fifofd, EPOLLIN);

	//3. 使用epoll_wait开始监测
	while (1)
	{
		int cnt = epoll_wait(epfds, evs, EPOLL_FD_MAX, -1);
		if (cnt < 0)
		{
			perror("fail epoll_wait");
			return -1;
		}
		for (int i = 0; i < cnt; i++)
		{
			if (0 == evs[i].data.fd)
			{
				fgets(buff, sizeof(buff), stdin);
				printf("STDIN: %s\n", buff);

			}
			else if (fifofd == evs[i].data.fd)
			{
				memset(buff, 0, sizeof(buff));
				read(fifofd, buff, sizeof(buff));
				printf("FIFO: %s\n", buff);

			}
		}

	}
	close(fifofd);
	return 0;
}