linux下多进程并发服务器

多进程并发服务器

  1 /*================================================================
  2  *   Copyright (C) 2020 hqyj Ltd. All rights reserved.
  3  *                               
  4  *   文件名称:01_多进程server.c
  5  *   创 建 者:Chens             
  6  *   创建日期:2020年04月24日    
  7  *   描    述:多进程并发服务器  
  8  *                               
  9  ================================================================*/
 10                                  
 11                                  
 12 #include <stdio.h>               
 13 #include <stdint.h>              
 14 #include <sys/types.h>           
 15 #include <sys/socket.h>          
 16 #include <stdlib.h>              
 17 #include <unistd.h>              
 18 #include <arpa/inet.h>           
 19 #include <string.h>              
 20 #include <errno.h>               
 21 #include <sys/types.h>           
 22 #include <sys/socket.h>          
 23 #include <sys/types.h>           
 24 #include <sys/wait.h>            
 25                                  
 26 #define  SERV_PORT    6666       
 27 #define  SERV_IP_ADDR "192.168.1.104"
 28 #define  QUIT         "quit"     
 29                                  
 30 #define BACKLOG       10         
 31                                  
 32 void sig_child_handle(int signo);
 33 void cli_info(struct sockaddr_in cin);
 34 void cli_data_handle(void* arg); 
 35                                  
 36                                  
 37 int main(int argc, char *argv[]) 
 38 {                                
 39     signal(SIGCHLD,sig_child_handle);
 40     //子进程结束后,会给父进程发送这个信号
 41     //捕捉,并执行处理           
 42                                  
 43     //创建socket                 
 44     int fd =-1;                  
 45     if( (fd = socket(AF_INET,SOCK_STREAM,0))<0){
 46         perror("socket");        
 47         exit(1);                 
 48     }                            
 49                                  
 50     //允许地址快速重用           
 51     int b_reuse = 1;             
 52     setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
 53                                  
 54     //绑定                       
 55     struct sockaddr_in sin;      
 56     bzero(&sin,sizeof(sin));     
 57     sin.sin_family          = AF_INET;
 58     sin.sin_port            = htons(SERV_PORT);
 59     sin.sin_addr.s_addr     = htonl(INADDR_ANY);
 60                                  
 61     if(bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
 62         perror("bind");          
 63         exit(1);                 
 64     }                            
 65                                  
 66     //监听                       
 67     if(listen(fd,BACKLOG) <0){   
 68         perror("listen");        
 69         exit(1);                 
 70     }                            
 71                                  
 72     struct sockaddr_in cin;      
 73     socklen_t addrlen = sizeof(cin);
 74     int newfd = -1;              
 75     pid_t pid = -1;              
 76     while(1){                    
 77         bzero(&cin,sizeof(cin)); 
 78         newfd = accept(fd,(struct sockaddr *)&cin,&addrlen);
 79         if(newfd <0){            
 80             perror("accept");    
 81             continue;            
 82         }                        
 83                                  
 84         /*创建子进程用来处理已建立链接的客户端数据收发*/
 85         if((pid=fork())<0){      
 86             perror("fork");      
 87             exit(1);             
 88         }                        
 89         if(0 == pid)//子进程     
 90         {                        
 91             close(fd);           
 92             //已连接客户端信息   
 93             cli_info(cin);       
 94             //调用收发函数       
 95             cli_data_handle(&newfd);
 96             return 0;            
 97         }                        
 98         else{                    
 99             close(newfd);        
100         }                        
101     }                            
102                                  
103     close(fd);                   
104     return 0;                    
105 }                                
106                                  
107 void sig_child_handle(int signo){
108     if(SIGCHLD == signo){        
109         while(waitpid(-1,NULL,WNOHANG)>0);
110     }                            
111 }                                
112                                  
113                                  
114 void cli_info(struct sockaddr_in cin)
115 {                                
116     //显示客户端信息             
117     char ipv4_addr[16];          
118     bzero(ipv4_addr,sizeof(ipv4_addr));
119     if(NULL == inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,ipv4_addr,sizeof(ipv4_addr))){
120         perror("inet_ntop");     
121         exit(1);                 
122     }                            
123     printf("客户端(%s:%d):已连接\n",ipv4_addr,ntohs(cin.sin_port));
124 }                                
125                                  
126                                  
127                                  
128 void cli_data_handle(void* arg)  
129 {                                
130     int newfd = *((int*)arg);    
131     printf("子进程连接newfd=%d\n",newfd);
132                                  
133     int ret = -1;                
134     char buf[BUFSIZ]={};         
135     while(1){                    
136         bzero(buf,BUFSIZ);       
137         do{                      
138             ret=read(newfd,buf,BUFSIZ-1);
139         }while(ret<0&&EINTR==errno);
140         if(ret<0){               
141             perror("recv");      
142             exit(1);             
143         }                        
144         if(!ret)break;           
145                                  
146         printf("收到:%s\n",buf);
147                                  
148         if(!strncasecmp(buf,QUIT,strlen(QUIT))){
149             puts("++++客户端退出了");
150             break;               
151         }                        
152     }                            
153     close(newfd);                
154 }                                
155                                  
156                                  

多线程并发服务器

  1 /*================================================================
  2 *   Copyright (C) 2020 hqyj Ltd. All rights reserved.
  3 *   
  4 *   文件名称:01_多线程并发server.c
  5 *   创 建 者:Chens
  6 *   创建日期:2020年04月24日
  7 *   描    述:
  8 *
  9 ================================================================*/
 10  
 11  
 12 #include <stdio.h>
 13 #include <stdint.h>
 14 #include <sys/types.h>    
 15 #include <sys/socket.h>   
 16 #include <stdlib.h>
 17 #include <unistd.h>
 18 #include <arpa/inet.h>    
 19 #include <string.h>
 20 #include <errno.h>
 21 #include <sys/types.h>    
 22 #include <sys/socket.h>   
 23 #include <sys/types.h>    
 24 #include <sys/wait.h>     
 25 #include <pthread.h>      
 26  
 27 #define  SERV_PORT    6666
 28 #define  SERV_IP_ADDR "192.168.1.104"
 29 #define  QUIT         "quit"
 30  
 31 #define BACKLOG       10  
 32  
 33 void cli_info(struct sockaddr_in cin);
 34 void cli_data_handle(void *arg);
 35  
 36 int main(int argc, char *argv[])
 37 {
 38     //创建socket
 39     int fd =-1;
 40     if( (fd = socket(AF_INET,SOCK_STREAM,0))<0){
 41         perror("socket"); 
 42         exit(1);
 43     }
 44     puts("------创建套接字-----");
 45  
 46     //允许地址快速重用    
 47     int b_reuse = 1;      
 48     setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
 49     puts("------允许地址快速重用-----");
 50     //绑定
 51     struct sockaddr_in sin;
 52     bzero(&sin,sizeof(sin));
 53     sin.sin_family          = AF_INET;
 54     sin.sin_port            = htons(SERV_PORT);
 55     sin.sin_addr.s_addr     = htonl(INADDR_ANY);
 56  
 57     if(bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
 58         perror("bind");   
 59         exit(1);
 60     }
 61  
 62     puts("-------绑定ip端口-----");
 63     //监听
 64     if(listen(fd,BACKLOG) <0){
 65         perror("listen"); 
 66         exit(1);
 67     }
 68  
 69     puts("-------服务器开启监听------");
 70  
 71     struct sockaddr_in cin;
 72     socklen_t addrlen = sizeof(cin);
 73     int newfd =-1;
 74     pthread_t tid;
 75     while(1){
 76         newfd = accept(fd,(void*)&cin,&addrlen);
 77         if(newfd <0){     
 78             perror("accept");
 79             break;
 80         }
 81         //显示已连接信息  
 82         cli_info(cin);    
 83         
 84         //调用子线程      
 85         pthread_create(&tid,NULL,(void*)cli_data_handle,(void*)&newfd);
 86  
 87     }
 88     close(fd);
 89  
 90  
 91     return 0;
 92 }
 93  
 94 void cli_info(struct sockaddr_in cin)
 95 {
 96     //显示客户端信息      
 97     char ipv4_addr[16];   
 98     bzero(ipv4_addr,sizeof(ipv4_addr));
 99     if(NULL == inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,ipv4_addr,sizeof(ipv4_addr))){
100         perror("inet_ntop");
101         exit(1);
102     }
103     printf("客户端(%s:%d):已连接\n",ipv4_addr,ntohs(cin.sin_port));
104 }
105  
106 void cli_data_handle(void *arg)
107 {
108     pthread_detach(pthread_self());
109     int newfd = *((int *)arg);
110     printf("线程连接到newfd=%d\n",newfd);
111     int ret =-1;
112     char buf[BUFSIZ];     
113         while(1){
114         bzero(buf,BUFSIZ);
115         do{
116             ret=read(newfd,buf,BUFSIZ-1);
117         }while(ret<0&&EINTR==errno);
118         if(ret<0){
119             perror("recv");
120             exit(1);      
121         }
122         if(!ret)break;    
123                                                                                                      
124         printf("收到:%s\n",buf);
125  
126         if(!strncasecmp(buf,QUIT,strlen(QUIT))){
127             puts("++++客户端退出了");
128             break;
129         }
130     }
131     close(newfd);
132     pthread_exit(0);      
133 }

IO模型

在unix和Linux主要有4中IO模型

1、阻塞IO

​ 最常用、最简单、效率最低

​ 套接字建立后处于阻塞IO模式

​ read、recv、wirte、send、accept、connect。。。。

2、非阻塞IO

​ 可以防止阻塞在IO操作上,需要轮询。

​ 需要使用一个循环不停的测试是否资源文件有数据可以读取。

​ fcntl函数设置非阻塞状态

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
	fd:文件描述符
    cmd:F_GETFL和F_SETFL
返回值:
        F_GETFD返回文件描述符状态标记
        出错-1
int flag;
flag = fcntl(scokfd,F_GETFL,0);//获取文件状态标记
flag |= O_NONBLOCK;          //相当于flag = flag | O_NONBLOCK;
fcntl(sockfd,F_SETFL,flag);//设置文件标记
 1 /*================================================================
 2 *   Copyright (C) 2020 hqyj Ltd. All rights reserved.
 3 *   
 4 *   文件名称:01_非阻塞.c
 5 *   创 建 者:Chens
 6 *   创建日期:2020年04月24日
 7 *   描    述:
 8 *   
 9 ================================================================*/
10     
11     
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <strings.h>
16     
17 int main(int argc, char *argv[])
18 {   
19     int flag;
20     char buf[128]={};
21     //1获取文件描述符标记属性
22     flag = fcntl(0,F_GETFL);
23     //2设置                                                        
24     flag |= O_NONBLOCK;
25     //3写回去
26     fcntl(0,F_SETFL,flag);
27     
28     while(1){
29         bzero(buf,sizeof(buf));
30         fgets(buf,sizeof(buf),stdin);
31         sleep(1);
32         printf("------buf:%s\n",buf);
33     }
34     
35     return 0;
36 }   

3、信号驱动

​ 异步通讯模型

​ 信号驱动IO是指进程提前告诉内核,当某个文件描述符上发生了事件,内核以信号的方式通知进程。总的来说,信号驱动IO对TCP套接字几乎是无用,因为这个信号产生的过于频繁,不能区分是哪种事件。

4、IO多路复用

​ 允许同时对多个IO进行监控

​ IO多路复用是只让内核一旦发现指定的一个或多个IO产生特定的事件,才通知这个进程。

​ 为什么要用多路复用

​ 相对于多进程、多线程、非阻塞系统开销小,而且不用维护进程和线程等

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

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

int select(int maxfd, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);
参数:
    maxfd:要监控的文件描述符值最大那个 +1
    readfds:指定要监控的读集合
    writefds:写集合
	exceptfds:异常集合
	关于集合:我们想添加fd到集合、从集合中删除fd、查询fd是不是在集合中、清空集合

		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);清空集合

	timeout:超时设置
    	NULL:一直阻塞,直到有文件描述符产生特定事件函数返回
    	返回值:-1出错,>0产生事件
    设置超时时间:
    	0立即返回非阻塞
    	时间不为零,等着,超时就返回
    	返回值0时间到了-1出错>0产生事件
    struct timeval{
		long tv_sec;
         long tv_usec;
    }

返回值:
    产生事件的文件描述符个数
    失败-1
 1 /*================================================================
 2  *   Copyright (C) 2020 hqyj Ltd. All rights reserved.
 3  *                          
 4  *   文件名称:02_多路复用01.c
 5  *   创 建 者:Chens        
 6  *   创建日期:2020年04月24日
 7  *   描    述:执行的时候需要加sudo
 8  *                          
 9  ================================================================*/
10                             
11                             
12 #include <stdio.h>          
13 #include <sys/time.h>       
14 #include <sys/types.h>      
15 #include <unistd.h>         
16 #include <sys/types.h>      
17 #include <sys/stat.h>       
18 #include <fcntl.h>          
19 #include <stdlib.h>         
20                             
21 int main(int argc, char *argv[])
22 {                           
23     //打开鼠标驱动文件      
24     int fd =-1;             
25     fd = open("/dev/input/mouse0",O_RDONLY);
26     if(fd < 0){             
27         perror("open");     
28         exit(1);            
29     }                       
30                             
31     //创建关心的读集合      
32     fd_set readfds,temp;    
33     //给集合清零初始化      
34     FD_ZERO(&readfds);      
35     //将关心的文件描述符添加到集合中
36     FD_SET(0,&readfds);     
37     FD_SET(fd,&readfds);    
38                             
39     struct timeval tv={1,0};                                       
40                             
41     int val =-1;            
42                             
43     while(1){               
44         tv.tv_sec  = 1;     
45         tv.tv_usec = 0;     
46         temp = readfds;     
47         val = select(fd+1,&temp,NULL,NULL,&tv);
48         if(val <0){         
49             perror("select");
50             exit(1);        
51         }                   
52         else if(0 == val){  
53             puts("-------超时了");
54             continue;       
55         }                   
56         else{               
57             if(FD_ISSET(0,&temp)){
58                 puts("键盘输入++++++++");
59             }               
60             if(FD_ISSET(fd,&temp)){
61                 puts("****鼠标动了***");
62             }               
63         }                   
64         sleep(1);           
65     }                       
66     close(fd);              
67     return 0;               
68 }                           
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值