当前位置: 首页 > article >正文

自学Linux系统软件编程第八天

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

UDP:无连接

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

TCP:有连接

构建TCP并发服务器: 让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;
}

子进程结束会隐含一个SIGCHLD信号,对该信号进行注册,然后跳转到该函数进行操作。

方法二:多线程

资源消耗相比多进程的较小

--------------------------------------------------------------实现-------------------------------------------------------------

#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;
}

 方法三:线程池

提前预创建大量线程,避免任务执行过程中创建线程的耗时。

方法四:IO多路复用(转接)

在不创建新的进程和线程的情况下,可以在一个进程中同时监测多个IO

===============“黑心饭店老板和一个牛马服务员的悲惨故事”=========================

#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函数:


/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

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

功能:阻塞等待IO事件,返回事件结果
参数:nfds:关注的最大文件描述符+1;
    readfds:文件描述符读事件的集合表
    writefds:文件描述符写事件的集合表
    exceptfds:其他事件表
    timeout:超时时间(如果不设置超时时间,则该位置为NULL)即select的等待时间
返回值:成功返回到达的文件描述符事件个数,失败返回-1,如果超时时间到达还没有IO事件时返回0

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

实现多路复用的过程(select,poll,epoll)

1.创建文件描述符集合

2.将关注的文件描述符加入到集合

3.等待IO时间到达

4.根据不同的IO事件处理不同的任务

select的不足之处:

1.底层使用位图管理文件描述符,最多允许同时监测1024个文件描述符(有上限)

2.文件描述符集合在应用层创建需要实现应用层和内核层的反复拷贝。

3.需要应用层对集合表进行遍历,循环找到达的事件。

4.只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式)。

QUESTION:

完成多线程时,代码写完线程的时候不知道怎么关闭线程,如果就放在while(1)里面,就会阻塞后面的进程,那该怎么解决呢,我就查询了一些资料,发现可以对线程进行分离,

 


http://www.kler.cn/a/573991.html

相关文章:

  • 数据结构 常见的排序算法
  • python爬虫:python中使用多进程、多线程和协程对比和采集实践
  • 【C++】5.4.3 范围for语句
  • maven高级-02.继承与聚合-继承关系实现
  • 算阶,jdk和idea的安装
  • 程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<12>
  • Python中的常用库
  • Java中的集合
  • 国产ARM主机安装Ansible
  • vue3的npm配置修改源
  • DeepSeek携手防爆对讲机,大胆探索石化行业智能化升级
  • c#实现Modbus TCP/IP与RS 485数据交换的实例
  • 深入理解设计模式中的单例模式(Singleton Pattern)
  • 人工智能之数学基础:伴随矩阵
  • redis菜鸟教程
  • 【Linux篇】版本控制器-Git
  • nvidia驱动升级-ubuntu 1804
  • blender 渲染obj
  • 开发社交陪玩app小程序
  • 前端流式输出深度解析:技术原理、实战应用与性能优化