多进程并发服务器
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 }