linux网络编程之多进程、多线程

本文详细探讨了多线程和多进程在服务器模型中的应用,包括socket处理、fork后的套接字关闭,以及close与shutdown的区别。还介绍了线程和协程的调度开销,并给出了相关代码示例。通过对比,阐述了线程和协程在内存占用和上下文切换成本上的优劣。最后提到了端口复用和线程池的概念,以及协程作为轻量级线程的优势。
摘要由CSDN通过智能技术生成

多线程处理

优点
在一个程序中,很多操作是非常耗时的,使用多线程,可以将耗时的任务在后台执行,同时执行其他操作。
可以提高程序的效率。

缺点:
太多线程,耗费系统资源,需要开辟每次和CPU调度;
太多线程,影响性能,多任务间切换

先read 函数的返回值 非常重要

> 0 实际读到的字节数

= 0 已经读到结尾(对端已经关闭)

-1 应进一步判断errno的值:重要!!! -1一定要进行判断,否则接收端一直在接收

	errno = EAGAIN or EWOULDBLOCK 设置了非阻塞方式读,没有数据到达。 

	errno = EINTR 慢速系统调用被 中断。

	errno = “其他情况” 异常。

多进程模型

socket()
bind()
listen()
//catch sigchild signal 回收子进程
while(1)
{
	cfd=accept()
	pid=fork()
	if(pid==0)//child process
	{
		close(lfd)
		read()/write()
	}
	else if(pid > 0)//parent process
	{
		close(cfd)
	}
	else
	{
		//error
	}
}

fork之后的关闭套接字为什么是两个

socket描述符的引用计数,在执行完socket() accept()后, 引用计数分别为 1 、1。
fork()后 子进程复制了父进程的socket描述符,引用数增加,引用计数分别为2,2。
fork()返回值0代表子进程中,关闭子进程中的lfd(close(lfd));父进程中关闭子进程中的cfd。

保证了子进程处理与客户的连接。

shutdown close区别

close只有在对应socket的引用计数为0时,才会真正发送FIN报文来关闭这个连接,shutdown没有这个限制,直接发送FIN报文

close同时终止了读和写两个方向的数据传输。TCP是双工的,有时候值接收数据,不发送数据,使用shutdown可以指定关闭读端或者写端。

demo

#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<sys/un.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<assert.h>
#include<string.h>
#include<ctype.h>
#include<errno.h>
#include<sys/wait.h>
#include<errno.h>

#define SERVER_PORT 6666 
#define MAXLINE 1024


void catch_child(int signum)
{
	while(waitpid(0,NULL,WNOHANG) > 0);
	return;
}


static void sys_error(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc,char *argv[])
{
	int lfd = 0,cfd = 0;
	int ret;
	char buffer[BUFSIZ],client_ip[56];
	pid_t pid;
	char str[1024];

	struct sockaddr_in serv_addr,clit_addr;
	socklen_t clit_addr_len,client_ip_len;
	//signal
	struct sigaction act;
	act.sa_handler = catch_child;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	ret = sigaction(SIGCHLD,&act,NULL);
	if(ret != 0)
	{
		sys_error("sigaction error");
	}

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERVER_PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	lfd = socket(AF_INET,SOCK_STREAM,0);
	if(lfd == -1)
		sys_error("socket error");

	int opt = 1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

	bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

	listen(lfd,128);
    socklen_t client_addrlength = sizeof(clit_addr);
	client_addrlength = sizeof(clit_addr);
	
	while(1){
        	cfd = accept(lfd,(struct sockaddr *)&clit_addr,&client_addrlength);
        	if(cfd < 0)
            {
                printf("error is:%d\n",errno);
		    }

        	pid = fork();
        	if (pid == 0) {//child
            	close(lfd);
            	while (1) {
                	ret = read(cfd, buffer, MAXLINE);
                	if (ret == 0) {
                    	printf("the other side has been closed.\n");
                    	break;
					}
					else if(ret == -1)
					{
						//printf("str=%s\n", strerror(errno));
						if (errno == EINTR)
                        		continue;
	            	 	else
                        	return -1;
					}
                write(cfd, buffer, ret);
            }
           	 	close(cfd);
            	return 0;
        	} else if (pid > 0) {//parent
            	close(cfd);
        	}  
			else
			{
		   	 	perror("error exit\n");
	            exit(-1);
			}
		}
        return 0;
}

多线程

对多进程的服务器的改进,多进程需要消耗较大的资源。数量级1:10000,共享 全局变量信息,带来同步问题。

多线程模型

socket()
bind()
listen()
while(1)
{
	cfd = accept(lfd,);
	pthread_create(&pid,NULL,fun,(void* )cfd);
	prhread_detach(tid);//子线程分离
}
//child pthead
void *fun()
{
	read()/write()
	pthread_exit();
}

改进方案:线程池,协程

demo

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include<errno.h>

#define MAXLINE 1024
#define SERV_PORT 6666

struct s_info {                    
    struct sockaddr_in cliaddr;
    int connfd;
};

void *work_pthread(void *arg)
{
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];     
    
    while (1) {
        n = read(ts->connfd, buf, MAXLINE);                    
        if (n == 0) {
            printf("the client %d closed...\n", ts->connfd);
            break;                                             
        } 
        else if(ret == -1)
		{
				//printf("str=%s\n", strerror(errno));
				if (errno == EINTR)
                  		continue;
	            else
                        return -1;
		}            
        write(ts->connfd, buf, n);  //回写
    }
    close(ts->connfd);
    return (void *)0;
}

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    pthread_t tid;

    struct s_info ts[256]; 
    int i = 0;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);                  
    bzero(&servaddr, sizeof(servaddr));                           
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);                              
    servaddr.sin_port = htons(SERV_PORT);     
    
    int opt = 1;
	setsockopt(listenfd ,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
                                      
    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));             
    listen(listenfd, 128);                                                      

    printf("Accepting client connect ...\n");

    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfid = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); 
        if(connfid < 0)
		{
			printf("error is %d\n",errno);
		}
		ts[i].cliaddr = cliaddr;
        ts[i].connfd = connfd;

        pthread_create(&tid, NULL, work_pthread, (void*)&ts[i]);
        pthread_detach(tid);                                                    
        i++;
    }
    return 0;
}

端口复用,在断开连接后,端口号可以再次被使用,否则要等到40多秒

int opt = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

协程

那什么是协程呢?协程 Coroutines 是一种比线程更加轻量级的微线程。

线程与线程的调度开销对比

线程调度需要保存一个用户线程的状态,恢复另一个线程状态到寄存器,涉及用户态和内核态的转换。

协程拥有自己的寄存器上下文和栈,调度时将寄存器上下文和栈保存带其他地方,在切换回来时,恢复先前保存的寄存器上下文。直接操作用户空间栈,没有内核切换的开销。

优点

内存占用要小,且创建开销要小
减少上下文切换的开销

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了维护世界和平_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值