标签:linux统系统开发11 socket api编程2 多进程 多线程 高并发处理
【本文谢绝转载原文来自http://990487026.blog.51cto.com】
<纲要> Linux统系统开发11 Socket API编程2 多进程 多线程 高并发处理 UDP服务器 客户端最小模型,处理字符转大写 TCP 多进程并发服务器模型,为每个客户端开启一个进程: TCP 多线程服务器模型,使用wrap函数封装 作业:
----------------------------------------------------
UDP服务器 客户端最小模型,处理字符转大写
chunli@ubuntu:~/linux_c/network_2$ cat server_udp.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <unistd.h> #include <ctype.h> #define SERVER_PORT 8000 int main(void) { int sockfd; struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; char buf[1024]; int len = 0; socklen_t socketlen = 0; //char ipstr[INET6_ADDRSTRLEN]; //ipV6字符串 char ipstr[INET_ADDRSTRLEN]; //ipV4字符串最大16个字符 int strlen = 0; sockfd = socket(AF_INET,SOCK_DGRAM,0); //构建用于UDP通信的套接字 bzero(&serveraddr,sizeof(serveraddr)); //清零结构体数据 serveraddr.sin_family = AF_INET; //IPV4 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//允许来自本地任意IP的请求,INADDR_ANY 就是0 serveraddr.sin_port = htons(SERVER_PORT); bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); while(1) { socketlen = sizeof(clientaddr);//初始化 len = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&clientaddr,&socketlen); //clientaddr客户机信息 inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr));//大端转本地 ip地址 ntohs(clientaddr.sin_port); //转端口 printf("client ip:%s port:%d\n",ipstr,0); int i = 0; while(i < len) { buf[i] = toupper(buf[i]); i++; } sendto(sockfd,buf,len,0,(struct sockaddr*)&(clientaddr),sizeof(clientaddr)); } close(sockfd); return 0; } chunli@ubuntu:~/linux_c/network_2$ gcc -o server_udp server_udp.c && ./server_udp
chunli@ubuntu:~/linux_c/network_2$ cat client_udp.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define SERVER_PORT 8000 int main(int argc,char **argv) { if(argc < 2) { printf("to few argc \n"); exit(1); } struct sockaddr_in serveraddr; int clientfd; char buf[4096]; char ipstr[] = "11.11.11.3 "; //服务器IP //1创建socket clientfd = socket(AF_INET,SOCK_DGRAM,0); //UDP //2初始化服务器地址 bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family =AF_INET; inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr); serveraddr.sin_port = htons(SERVER_PORT); //3连接服务器 //udp不需要连接服务器 //4 请求服务器处理数据 int i = 1; while(i < argc) { bzero(buf,sizeof(buf)); sendto(clientfd,argv[i],strlen(argv[i]),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr)); recvfrom(clientfd,buf,sizeof(buf),0,NULL,0); printf("%s ",buf); i++; } printf("\n"); //5 关闭socket close(clientfd); return 0; } chunli@ubuntu:~/linux_c/network_2$ gcc -o client_udp client_udp.c && ./client_udp Hello World! Linux HELLO WORLD! LINUX chunli@ubuntu:~/linux_c/network_2$ gcc -o client_udp client_udp.c && ./client_udp lichunli LICHUNLI chunli@ubuntu:~/linux_c/network_2$
服务端的输出
chunli@ubuntu:~/linux_c/network_2$ gcc -o server_udp server_udp.c && ./server_udp client ip:127.0.0.1 port:0 client ip:127.0.0.1 port:0 client ip:127.0.0.1 port:0 client ip:127.0.0.1 port:0
-----------------------------------------------------
TCP 多进程并发服务器模型
TCP 多进程并发服务器模型,为每个客户端开启一个进程:
使用多进程并发服务器时要考虑以下几点:
1.父最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)
2.系统内创建进程个数(内存大小相关)
3.进程创建过多是否降低整体服务性能(进程调度)
1,服务端程序:
chunli@ubuntu:~/linux_c/m_fork$ cat server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <ctype.h> #include <unistd.h> #include <arpa/inet.h> int main(void) { struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; int socketfd; int clientfd; int addrlen; int port; char ipstr[128]; char buf[4096]; //字符缓冲 int len = 0; pid_t pid; //多线程 //1.socket socketfd = socket(AF_INET,SOCK_STREAM,0); //建立TCP //2.bind bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family = AF_INET; //地址族协议IPV4 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址 serveraddr.sin_port = htons(8000); //端口 bind(socketfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); //3.listen listen(socketfd,128); while(1) { //4.appept阻塞模式监听 addrlen = sizeof(clientaddr); clientfd = accept(socketfd,(struct sockaddr*)&clientaddr,(socklen_t*)&addrlen);//发返回客户端socket //输出客户端Ip地址 和端口号 inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr));//ip数字转字符 port = ntohs(clientaddr.sin_port); //大端转小端 printf("client ip %s ,port:%d \n",ipstr,port); //多进程处理客户请求 pid = fork(); if(pid == 0) //子进程 { close(socketfd);//关闭子进程的监听 while(1) { //处理客户请求 len = read(clientfd,buf,sizeof(buf)); int i = 0; while(i < len) { buf[i] = toupper(buf[i]); i++; } write(clientfd,buf,len); } //5再见 close(clientfd); return 0; } else if(pid > 0) //父进程 { close(clientfd);//关闭父进程的客户端socket } else { perror("fork"); exit(1); } } close(socketfd); return 0; } chunli@ubuntu:~/linux_c/m_fork$ gcc server.c -o server -Wall && ./server 再开一个本机窗口,查看监听8000:(因为程序过于简陋,有时候起可能起不来) chunli@ubuntu:~/linux_c/m_fork$ netstat -tnlp (并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户) 激活Internet连接 (仅服务器) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 8193/server tcp6 0 0 :::22 :::* LISTEN - chunli@ubuntu:~/linux_c/m_fork$
客户端程序:
chunli@ubuntu:~/linux_c/m_fork$ cat client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define SERVER_PORT 8000 int main(int argc,char **argv) { // if(argc < 2) // { // printf("to few argc \n"); // exit(1); // } // struct sockaddr_in serveraddr; int clientfd; char buf[4096]; char ipstr[] = "11.11.11.3"; //服务器IP //write(STDOUT_FILENO,ipstr,strlen(ipstr)+1); exit(1); //1创建socket clientfd = socket(AF_INET,SOCK_STREAM,0); //TCP //2初始化服务器地址 bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family =AF_INET; inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr); serveraddr.sin_port = htons(SERVER_PORT); //3连接服务器 connect(clientfd,(const struct sockaddr *)&serveraddr,sizeof(serveraddr)); //4 请求服务器处理数据 while(1) { fgets(buf,sizeof(buf),stdin); write(clientfd,buf,strlen(buf)); int len = read(clientfd,buf,sizeof(buf)); write(STDOUT_FILENO,buf,len); //printf("%s\n",buf); } //5 关闭socket close(clientfd); return 0; }
在局域网内的其他linux主机跑一跑:
chunli@ubuntu:~/linux_c/m_fork$ gcc client.c -o client -Wall -g && ./client haha HAHA lslsls LSLSLS
局域网其他主机跑的结果:
chunli@ubuntu14:~/m_fork$ gcc client.c -o client -Wall -g && ./client opopop OPOPOP Hello World! HELLO WORLD!
再回来看看服务端输出的信息:
chunli@ubuntu:~/linux_c/m_fork$ gcc server.c -o server -Wall && ./server client ip 11.11.11.3 ,port:35218 client ip 11.11.11.8 ,port:41416
数据反映了有两个客户端在耍闹。
【程序bug】客户端断开之后,服务端3个进程还在运行:
chunli@ubuntu:~$ ps aux | grep ./server chunli 8368 0.0 0.0 4356 640 pts/14 S+ 14:40 0:00 ./server chunli 8376 0.0 0.0 4356 92 pts/14 S+ 14:40 0:00 ./server chunli 8384 0.0 0.0 4356 92 pts/14 S+ 14:41 0:00 ./server chunli 8388 0.0 0.0 21312 896 pts/13 R+ 14:43 0:00 grep --color=auto ./server chunli@ubuntu:~$ chunli@ubuntu:~$ netstat -apn | grep 8000 (并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户) tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 8368/server tcp 0 0 11.11.11.3:8000 11.11.11.3:35222 ESTABLISHED 8376/server tcp 0 0 11.11.11.3:35222 11.11.11.3:8000 ESTABLISHED 8375/client tcp 0 0 11.11.11.3:8000 11.11.11.8:41417 ESTABLISHED 8384/server chunli@ubuntu:~$
---------------------------------------------------
TCP 多线程并发服务器,使用wrap函数封装
TCP 多线程服务器模型,使用wrap函数封装
在使用线程模型开发服务器时需考虑以下问题:
1.调整进程内最大文件描述符上限
2.线程如有共享数据,考虑线程同步
3.服务于客户端线程退出时,退出处理。(退出值,分离态)
4.系统负载,随着链接客户端增加,导致其它线程不能及时得到CPU
1,服务端程序
chunli@ubuntu:~/linux_c/m_pthread$ cat server_pthread.c //gcc server_pthread.c wrap.c -lpthread -Wall #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include <ctype.h> #include "wrap.h" #define MAXLINE 80 #define SERVER_PORT 8000 struct s_info { struct sockaddr_in cliaddr; int connfd; }; void *do_work(void *arg) { int n = 0; int i = 0; struct s_info *ts = (struct s_info*)arg; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; /* 在创建线程前设置分离属性比在创建线程后设置分离状态效率高*/ pthread_detach(pthread_self()); //本示例在线程创建完成后完成自我分离 while(1) { n = Read(ts->connfd,buf,MAXLINE); if(n == 0) { printf("the other side has been closed.\n"); break; } printf("received from %s at PORT %d\n", inet_ntop(AF_INET,&(*ts).cliaddr.sin_addr,str,sizeof(str)), ntohs((*ts).cliaddr.sin_port)); for(i = 0;i < n;i++) { buf[i] = toupper(buf[i]); } Write(ts->connfd,buf,n); } Close(ts->connfd); return NULL; //函数返回值需要返回一个东西 } int main(void) { struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; socklen_t cliaddr_len; int listenfd = 0; int connfd = 0; int i = 0; pthread_t tid; struct s_info ts[383]; listenfd = Socket(AF_INET,SOCK_STREAM,0); bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family = AF_INET; //IPV4 serveraddr.sin_port =htons(SERVER_PORT);//设置监听端口 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//设置在哪个IP监听 Bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); Listen(listenfd,20); //The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow printf("Accepting connections....\n"); while(1) { cliaddr_len = sizeof(clientaddr); connfd = Accept(listenfd,(struct sockaddr*)&clientaddr,&cliaddr_len); ts[i].cliaddr = clientaddr; ts[i].connfd = connfd; /*达到线程最大数时,pthread_create 出错处理,增加服务器的稳定性 */ pthread_create(&tid,NULL,do_work,(void*)&ts[i]); i++; } return 0; }
2,客户端程序:
chunli@ubuntu:~/linux_c/m_pthread$ cat client.c #include <stdio.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include "wrap.h" #define MAXLINE 80 #define SERVER_PORT 8000 int main(int argc ,char **argv) { struct sockaddr_in serveraddr; char buf[MAXLINE]; int sockfd = 0; int n = 0; sockfd = Socket(AF_INET,SOCK_STREAM,0); bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family = AF_INET; inet_pton(AF_INET,"11.11.11.3",&serveraddr.sin_addr.s_addr); serveraddr.sin_port = htons(SERVER_PORT); Connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)); while(fgets(buf,MAXLINE,stdin) != NULL ) { Write(sockfd,buf,strlen(buf)); n = Read(sockfd,buf,MAXLINE); if(n == 0) { printf("the other side has been close.\n"); } else { Write(STDOUT_FILENO,buf,n); } } Close(sockfd); return 0; }
依赖的wrap库源代码
chunli@ubuntu:~/linux_c/m_pthread$ cat wrap.h /* wrap.h */ #ifndef __WRAP_H_ #define __WRAP_H_ void perr_exit(const char *s); int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); void Bind(int fd, const struct sockaddr *sa, socklen_t salen); void Connect(int fd, const struct sockaddr *sa, socklen_t salen); void Listen(int fd, int backlog); int Socket(int family, int type, int protocol); ssize_t Read(int fd, void *ptr, size_t nbytes); ssize_t Write(int fd, const void *ptr, size_t nbytes); void Close(int fd); ssize_t Readn(int fd, void *vptr, size_t n); ssize_t Writen(int fd, const void *vptr, size_t n); ssize_t my_read(int fd, char *ptr); ssize_t Readline(int fd, void *vptr, size_t maxlen); #endif
chunli@ubuntu:~/linux_c/m_pthread$ cat wrap.c /* wrap.c */ #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> void perr_exit(const char *s) { perror(s); exit(1); } int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) { int n; again: if ( (n = accept(fd, sa, salenptr)) < 0) { if ((errno == ECONNABORTED) || (errno == EINTR)) goto again; else perr_exit("accept error"); } return n; } void Bind(int fd, const struct sockaddr *sa, socklen_t salen) { if (bind(fd, sa, salen) < 0) perr_exit("bind error"); } void Connect(int fd, const struct sockaddr *sa, socklen_t salen) { if (connect(fd, sa, salen) < 0) perr_exit("connect error"); } void Listen(int fd, int backlog) { if (listen(fd, backlog) < 0) perr_exit("listen error"); } int Socket(int family, int type, int protocol) { int n; if ( (n = socket(family, type, protocol)) < 0) perr_exit("socket error"); return n; } ssize_t Read(int fd, void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = read(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } ssize_t Write(int fd, const void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = write(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } void Close(int fd) { if (close(fd) == -1) perr_exit("close error"); } ssize_t Readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) break; nleft -= nread; ptr += nread; } return n - nleft; } ssize_t Writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } static ssize_t my_read(int fd, char *ptr) { static int read_cnt; static char *read_ptr; static char read_buf[100]; if (read_cnt <= 0) { again: if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return -1; } else if (read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1; } ssize_t Readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == ‘\n‘) break; } else if (rc == 0) { *ptr = 0; return n - 1; } else return -1; } *ptr = 0; return n; }
服务端编译运行:
chunli@ubuntu:~/linux_c/m_pthread$ gcc server_pthread.c wrap.c -lpthread -Wall -o server && ./server Accepting connections....
服务端查看端口监听:
chunli@ubuntu:~/linux_c/m_fork$ netstat -tnlp Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 9047/server tcp6 0 0 :::22 :::* LISTEN -
客户端1编译运行:
chunli@ubuntu:~/linux_c/m_pthread$ gcc -Wall -o client client.c wrap.c && ./client haha HAHA English ENGLISH hehe HEHE
客户端2编译运行:
客户端2编译运行: chunli@ubuntu14:~/m_pthread$ gcc -Wall -o client client.c wrap.c && ./client ls LS hello HELLO china CHINA
服务端输出信息:
chunli@ubuntu:~/linux_c/m_pthread$ gcc server_pthread.c wrap.c -lpthread -Wall -o server && ./server Accepting connections.... received from 11.11.11.3 at PORT 35224 received from 11.11.11.8 at PORT 41418 received from 11.11.11.8 at PORT 41418 received from 11.11.11.8 at PORT 41418 received from 11.11.11.3 at PORT 35224 received from 11.11.11.3 at PORT 35224
服务端线程信息
chunli@ubuntu:~/linux_c/m_fork$ ps -eLf | grep server chunli 9047 8294 9047 0 3 17:18 pts/13 00:00:00 ./server chunli 9047 8294 9085 0 3 17:20 pts/13 00:00:00 ./server chunli 9047 8294 9099 0 3 17:22 pts/13 00:00:00 ./server
服务器线程与客户端连接关系:
chunli@ubuntu:~/linux_c/m_fork$ netstat -anp | grep 8000 tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 9047/server tcp 0 0 11.11.11.3:8000 11.11.11.3:35224 ESTABLISHED 9047/server tcp 0 0 11.11.11.3:8000 11.11.11.8:41418 ESTABLISHED 9047/server tcp 0 0 11.11.11.3:35224 11.11.11.3:8000 ESTABLISHED 9084/client chunli@ubuntu:~/linux_c/m_fork$
ctrl+c 关闭一个客户端 看看服务端信息输出:
chunli@ubuntu:~/linux_c/m_pthread$ gcc server_pthread.c wrap.c -lpthread -Wall -o server && ./server Accepting connections.... received from 11.11.11.3 at PORT 35224 received from 11.11.11.8 at PORT 41418 received from 11.11.11.8 at PORT 41418 received from 11.11.11.8 at PORT 41418 received from 11.11.11.3 at PORT 35224 received from 11.11.11.3 at PORT 35224 the other side has been closed.
再看服务端线程信息
chunli@ubuntu:~/linux_c/m_fork$ ps -eLf | grep server chunli 9047 8294 9047 0 2 17:18 pts/13 00:00:00 ./server chunli 9047 8294 9085 0 2 17:20 pts/13 00:00:00 ./server
把服务端ctrl+c终止运行,再看看剩下的一个客户端输出信息
chunli@ubuntu:~/linux_c/m_pthread$ gcc -Wall -o client client.c wrap.c && ./client haha HAHA English ENGLISH hehe HEHE the other side has been close.
---------------------------------------------
作业:
1.编写获取网络服务器时钟服务器/客户机程序。
2.实现网络版本聊天室服务器客户机
本文出自 “魂斗罗” 博客,谢绝转载!
Linux统系统开发11 Socket API编程2 多进程 多线程 高并发处理
标签:linux统系统开发11 socket api编程2 多进程 多线程 高并发处理
原文地址:http://990487026.blog.51cto.com/10133282/1840391