Linux操作系统——TCP服务端并发模型

TCP:建立连接,一对一

要实现多任务并发,就引出了并发模型

一、多进程与多线程

1.在相同资源情况下,进程资源开销大,但其安全性高

2.线程相对于进程资源开销小,且并发量比进程大

①多进程并发基础代码

cs 复制代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

int init_tcp_http()    
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in cliaddr;
	socklen_t clilen = sizeof(cliaddr);

	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(51000);
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.183");
	int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (ret < 0)
	{
		perror("bind error");
		return -1;
	}

	
	ret = listen(sockfd, 10);
	if (ret < 0)
	{
		perror("listen error");
		return -1;
	}

    return sockfd;
}

int main(int argc, char const *argv[])
{
    struct sockaddr_in cliaddr;
    socklen_t clien = sizeof(cliaddr);


    int sockfd = init_tcp_http();
    if(sockfd < 0)
    {
        return -1;
    }
    while(1)
    {
        int connfd =accept(sockfd,(struct sockaddr *)&cliaddr,&clien);
        if(connfd < 0)
        {
            perror("accept error");
            return -1;
        }
        pid_t pid = fork();
        if(pid > 0)
        {

        }
        else if(pid == 0)
        {
            char buff[1024];
            
            while(1)
            {
                memset(buff,0,sizeof(buff));
                ssize_t cnt = recv(connfd,buff,sizeof(buff),0);
                if(cnt < 0)
                {
                    perror("recv error");
                    return -1;
                }
                else if(cnt == 0)
                {
                    printf("[%s : %d], online\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
                }
                printf("[%s : %d]  %s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);
                
                strcat(buff,"----->ok");
                cnt = send(connfd,buff,sizeof(buff),0);
                if(cnt < 0)
                {
                    perror("recv error");
                    return -1;
                }
            }
            close(connfd);
        }
    }
    
    close(sockfd);
    return 0;
}

②多线程并发基础代码实现

cs 复制代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>

int init_tcp_http()    
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket error");
		return -1;
	}
	
    int optval = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

	struct sockaddr_in cliaddr;
	socklen_t clilen = sizeof(cliaddr);

	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(51000);
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.183");
	int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (ret < 0)
	{
		perror("bind error");
		return -1;
	}

	
	ret = listen(sockfd, 10);
	if (ret < 0)
	{
		perror("listen error");
		return -1;
	}

    return sockfd;
}

void *task(void *arg)
{   
    int connfd = *(int *)arg;
    char buff[1024] = {0};
    while(1)
    {
        memset(buff,0,sizeof(buff));
        ssize_t cnt = recv(connfd,buff,sizeof(buff),0);
        if(cnt < 0)
        {
            perror("recv error");
            close(connfd);
            return NULL;
        }
        else if(cnt == 0)
        {
            printf("online\n");
            break;
        }
        printf("buff = %s\n",buff);

        strcat(buff,"----->ok");
        cnt = send(connfd,buff,sizeof(buff),0);
        if(cnt < 0)
        {
            perror("recv error");
            close(connfd);
            return NULL;
        }
    }
    close(connfd);
}


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

    struct sockaddr_in cliaddr;
    socklen_t clien = sizeof(cliaddr);

    int sockfd = init_tcp_http();
    if(sockfd < 0)
    {
        return -1;
    }
    while(1)
    {
        int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clien);
        if(connfd < 0)
        {
            perror("accept error");
            return -1;
        }
        
        pthread_create(&tid,NULL,task,&connfd);
        pthread_detach(tid); 

        
    }
    
    close(sockfd);
    return 0;
}

二、线程池

为了解决多线程或者多进程模型,在服务器运行过程,频繁创建和销毁线程(进程)带来的时间消耗问题。基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程框架。

简单的例子:主线程是一个餐厅,餐厅有四个服务员(此线程),当客人到餐厅就餐时,服务员前去接待,当四个服务员都没有空闲时,客人进行排队,就好比一个队列(先进先出),当服务员空闲来,就去"队列"中接待客人(task)

三、IO多路复用

I-->O:fd

对多个文件描述符的读写可以复用一个进程。

在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时监测

阻塞IO模式:

1. 多个任务之间是同步的效果

1)select

2)poll

3)epoll

2.相应函数解释

1)select实现IO多路复用:

  1. 创建文件描述符集合 fd_set

  2. 添加关注的文件描述符到集合 FD_SET();

  3. 使用select传递集合表给内核,内核开始监测事件 select()

  4. 当内核监测到事件时,应用层select将解除阻塞,并获得相关的事件结果

  5. 根据select返回的结果做不同的任务处理

位图为1024是因为文件描述符是0~1023(前三位是标准输出,标准输入,标准出错设备)

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);

这是系统自带的一些带参宏

FD_CLR 是将当前套接字置零

FD_ISSET 检测套接字是否置为1

FD_SET 将定义的套接字插入文件描述符集合

FD_ZERO 将文件描述符全清零
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,

struct timeval *timeout);

功能:传递文件描述符结合表给内核并等待获取事件结果

参数:

nfds : 关注的最大文件描述符+1

readfds: 读事件的文件描述符集合

writefds: 写事件的文件描述符集合

exceptfds:其他事件的文件描述符集合

timeout: 设置select监测时的超时时间

NULL : 不设置超时时间(select一直阻塞等待)

返回值:

成功:返回内核监测的到达事件的个数

失败:-1

0 : 超时时间到达,但没有事件发生,则返回0

相关推荐
用户516816614584114 分钟前
[VMware 无法检测此光盘中映像中的操作系统] VMware创建虚拟机无法检测操作系统iso镜像文件
linux·前端
MacroZheng15 分钟前
斩获 7.8K star!一款堪称开源监控新标杆的项目,牛皮!
java·linux·后端
YC运维43 分钟前
Ansible模块
java·服务器·前端
稳兽龙44 分钟前
codeforces(1045)(div2)D. Sliding Tree
c++·算法··路径树
倔强的石头1 小时前
java程序员如何搭建C++windows开发环境搭建(三)
c++·后端
博语小屋1 小时前
程序(进程)地址空间(1)
linux
yunyi1 小时前
使用acme.sh来实现自动化申请和续订TLS证书
linux·nginx·docker
未来可期LJ1 小时前
【Linux 小实战】自定义 Shell 的编写
linux·服务器
望获linux4 小时前
【Linux基础知识系列】第一百一十篇 - 使用Nmap进行网络安全扫描
java·linux·开发语言·前端·数据库·信息可视化·php