socket使用多进程实现并发的服务器
生活随笔
收集整理的這篇文章主要介紹了
socket使用多进程实现并发的服务器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
服務器測試效果:
andrew@andrew-Thurley:~/work/network$ bin/echo_tcp_server 8888 client: 127.0.0.1(55610) connected start read and write! name start read and write! name start read and write!客戶端測試效果:
andrew@andrew-Thurley:~/work/network$ bin/echo_tcp_server 8888 client: 127.0.0.1(55610) connected start read and write! name start read and write! name start read and write!創建的過程就是服務器接到連接請求就會創建一個進程來與該客戶端僅進行交互:
整個工程中使用到的文件樹形圖如下所示:
andrew@andrew-Thurley:~/work$ tree network network ├── bin │?? ├── echo_tcp_client │?? ├── echo_tcp_server │?? │?? ├── include │?? └── msg.h ├── Makefile ├── obj │?? └── msg.o ├── src├── echo_tcp_client.c├── echo_tcp_server.c├── msg.cecho_tcp_server.c文件
#include <netdb.h> #include <sys/socket.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <memory.h> #include <signal.h> #include <time.h> #include "msg.h" #include <sys/signal.h> #include <errno.h> #include <sys/types.h> #include <wait.h> #include <arpa/inet.h>/**************************************使用多進程處理并發***************************************/ //處理多個進程連接 //測試方法,運行程序,并指定端口號8888 //在另一個終端上使用 // telnet 127.0.0.1 8888進行連接 //127.0.0.1是沒有連接網絡時使用的本地回環ip地址/*聲明自定義函數*/ void sig_handler(int signo); void out_addr(struct sockaddr_in *clientaddr); void do_service(int fd); int sockfd;int main(int argc, char * argv[]) {if(argc < 2){printf("usage: %s #port\n",argv[0]);exit(1);}if(signal(SIGINT, sig_handler) == SIG_ERR) //開始捕捉信號 SIGINT{perror("signal sigint error!");exit(1);}/*步驟1創建socket*AF_INET IPV4*SOCK_STREAM 采用tcp協議*0 采用上面設置的協議類型*/sockfd = socket(AF_INET, SOCK_STREAM, 0); //使用默認協議if(sockfd < 0){perror("socket error!");exit(1);}/**步驟2,:調用bind函數將socket和地址(包括 IP,port)進行綁定**/struct sockaddr_in serveraddr; //聲明專用地址,需要的時候再轉換為通用地址memset(&serveraddr, 0, sizeof(serveraddr));//往地址填入ip、port、intnernet地址族類型serveraddr.sin_family = AF_INET; //IPV4serveraddr.sin_port = htons(atoi(argv[1])); //填入端口 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sockfd, (struct sockaddr *) &serveraddr,sizeof(serveraddr)) < 0){perror("bind error!");exit(1);}/* *步驟3:調用listen函數啟動端口監聽*通知系統去接受來自客戶端的連接請求*listen 的第二個參數10是請求隊列長度,將接收到的客戶端連接請求放到對應的隊列當中*/if(listen(sockfd, 10) < 0) //10監聽的隊列的上限{perror("listen error!");exit(1);}/**步驟4:調用accept函數,從隊列中獲得* 一個客戶端請求連接,并返回新的sock文件描述符fd,* 因為listen能夠監聽好多的連接請求,* 使用accept獲得一個連接使用* 若是accept沒有獲得客戶端連接,會進行阻塞,直到獲得客戶端連接*/struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);while(1){//使用循環就能夠在斷開與一個客戶端連接之后,在連接下一個客戶端連接int fd = accept(sockfd, (struct sockaddr*) &clientaddr,&clientaddr_len);if(fd < 0){perror("accept error!");continue;}/**步驟5: 啟動子進程調用IO函數(read/write)和連接的客戶端進行雙向通信*/pid_t pid = fork();if(pid < 0){continue;}else if(pid == 0) //子進程{//子進程會從父進程中繼承相應的文件描述符,指向同一個文件out_addr(&clientaddr);do_service(fd);/*步驟6:關閉套接字描述符*/close(fd);printf("child p is close\n");break; //子進程跳出循環后直接 return// exit(0);}else{//父進程/*步驟6:關閉套接字描述符*/close(fd);}}return 0; }void sig_handler(int signo) {if(signo == SIGINT){printf("server close\n");/*步驟6:關閉socket*/close(sockfd);exit(1);}if(signo == SIGCHLD){printf("child process deaded...\n");wait(NULL); //會自動回收子進程 wait函數中填入0說明對于回收的子進程不關心只是去回收子進程}}void out_addr(struct sockaddr_in *clientaddr) {/*將端口網絡字節序轉換為主機字節序 */ /*port是short類型數據*/int port = ntohs(clientaddr->sin_port);char ip[16];memset(ip, 0, sizeof(ip));/*將ip地址從網絡地址轉換為點分十進制*//*需要注意的地方#include <arpa/inet.h>const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);在函數中 const void *src說明第二個參數是指針類型的數據,但是當傳入clientaddr->sin_addr == (*clientaddr).sin_addr.s_addr就不在是一個指針類型數據因此需要使用&將其取址之后在傳入。*/inet_ntop(AF_INET, &clientaddr->sin_addr, ip, sizeof(ip));printf("client: %s(%d) connected\n",ip, port); }void do_service(int fd) {/*和客戶端雙向通訊,進行讀寫操作*/char buff[512];while(1){memset(buff, 0, sizeof(buff));printf("start read and write!\n");size_t size;if((size = read_msg(fd, buff, sizeof(buff))) < 0){perror("protocal error!");break;}else if (size == 0){break;}else{printf("%s\n", buff);if(write_msg(fd, buff, sizeof(buff)) < 0){if(errno == EPIPE)//若是對方已經關閉就直接跳出{perror("write_msg error!");break;}}}} }echo_tcp_client.c文件:
#include <netdb.h> #include <sys/socket.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <memory.h> #include <signal.h> #include <time.h> #include "msg.h" #include <sys/signal.h> #include <errno.h> #include <sys/types.h> #include <wait.h> #include <arpa/inet.h>int main(int argc, char * argv[]) {if(argc < 3){printf("usage:%s ip port\n",argv[0]);exit(1);}/*步驟1:創建socket*/int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error!");exit(1);}/*往serveraddr中填入ip,port和地址族類型(ipv4)*/struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));/*將字符串ip地址轉換為網絡字節序填入 serveraddr中*/inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);/**步驟2: 客戶端調用connect函數連接到服務器端*/if(connect(sockfd, (struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0){perror("connect error!");exit(1);}/*步驟3:調用IO函數,read和write和服務器進行雙向通信*/char buff[512];size_t size;char *prompt = ">";while(1){memset(buff, 0, sizeof(buff));write(STDOUT_FILENO, prompt, 1);size = read(STDIN_FILENO, buff, sizeof(buff));if(size < 0) continue;buff[size - 1] = '\0'; if(write_msg(sockfd, buff, sizeof(buff)) < 0){perror("write error!");continue; //就算寫出錯也結合運行 進行下次的發送}else {//客戶端發送成功if(read_msg(sockfd, buff, sizeof(buff)) < 0){perror("read msg error!");continue;}else{printf("%s\n",buff);}}}/*步驟4:關閉socket套接字*/close(sockfd);return 0; }msg.c文件:
#include "msg.h" #include <stdio.h> #include <unistd.h> #include <error.h> #include <memory.h> #include <sys/types.h>/*使用自定義協議進行數據傳輸*/static unsigned char msg_check(Msg *message);/* *發送一個基于自定義協議的message *發送的數據存放在buff中 */ extern int write_msg(int sockfd,char *buff, size_t len) {Msg message;memset(&message, 0, sizeof(message));strcpy(message.head, "iotek2012");memcpy(message.buff, buff, len);message.checknum = msg_check(&message);if(write(sockfd, &message, sizeof(message)) != sizeof(message)){return -1;} }/* *讀取一個基于自定義協議的message, *讀取的數據存放在buff中 */ extern int read_msg(int sockfd, char *buff, size_t len) {Msg message;memset(&message, 0, sizeof(message));size_t size;if((size = read(sockfd, &message, sizeof(message))) < 0){return -1;}else if(size == 0){return 0;}unsigned char s = msg_check(&message);if((s == (unsigned char)message.checknum) && (!strcmp("iotek2012", message.head))){strcpy(buff,message.buff);return sizeof(message);}return -1; }static unsigned char msg_check(Msg *message) {unsigned char s = 0;int i;for(i = 0; i < sizeof(message->head); i ++){s += message->head[i];}for(i = 0; i < sizeof(message->buff); i ++){s += message->buff[i];}return s; }msg.h文件:
#ifndef __MSH_H #define __MSG_H #include <stdio.h> #include <unistd.h> #include <error.h>typedef struct{//協議頭部char head[10];char checknum; //校驗碼//協議體部char buff[512]; //數據 }Msg;/* *發送一個基于自定義協議的message *發送的數據存放在buff中 */ extern int write_msg(int sockfd,char *buff, size_t len);/* *讀取一個基于自定義協議的message, *讀取的數據存放在buff中 */ extern int read_msg(int sockfd, char *buff, size_t len);#endifMakefile文件:
為了使思路清晰,在編寫Makefile文件的時候,沒有使用任何$<,$@,$^等自動化變量
.PHONY : all .PHONY : cleanall :bin/echo_tcp_server bin/echo_tcp_clientbin/echo_tcp_server : echo_tcp_servermv echo_tcp_server bin/echo_tcp_serverbin/echo_tcp_client : echo_tcp_clientmv echo_tcp_client bin/echo_tcp_clientecho_tcp_server : src/echo_tcp_server.c obj/msg.ogcc -o echo_tcp_server -Iinclude src/echo_tcp_server.c obj/msg.oecho_tcp_client : src/echo_tcp_client.c obj/msg.ogcc -o echo_tcp_client -Iinclude src/echo_tcp_client.c obj/msg.oobj/msg.o : msg.omv msg.o obj/msg.omsg.o : src/msg.c include/msg.hgcc -c -Iinclude src/msg.c?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的socket使用多进程实现并发的服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 专题导读:大数据隐私保护
- 下一篇: 作者:刘剑(1979-),男,中国联合网