socket中使用多线程创建并发服务器
生活随笔
收集整理的這篇文章主要介紹了
socket中使用多线程创建并发服务器
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
啟動服務(wù)器:
bin/echo_tcp_server_th 8888
啟動客戶端:
bin/echo_tcp_client 127.0.0.1 8888
使用ifconfig命令可以看到電腦的換回地址是127.0.0.1?? 因?yàn)榉?wù)器和客戶端在一臺電腦上使用換回地址進(jìn)行測試;
lo Link encap:本地環(huán)回 inet 地址:127.0.0.1 掩碼:255.0.0.0inet6 地址: ::1/128 Scope:HostUP LOOPBACK RUNNING MTU:65536 躍點(diǎn)數(shù):1接收數(shù)據(jù)包:2371 錯誤:0 丟棄:0 過載:0 幀數(shù):0發(fā)送數(shù)據(jù)包:2371 錯誤:0 丟棄:0 過載:0 載波:0碰撞:0 發(fā)送隊(duì)列長度:1000 接收字節(jié):287712 (287.7 KB) 發(fā)送字節(jié):287712 (287.7 KB)?
文件樹:
. ├── bin │?? ├── echo_tcp_client │?? ├── echo_tcp_server │?? ├── echo_tcp_server_th │?? ├── time_tcp_client │?? └── time_tcp_server ├── include │?? └── msg.h ├── Makefile ├── obj │?? └── msg.o ├── src │?? ├── echo_tcp_client.c │?? ├── echo_tcp_server.c │?? ├── echo_tcp_server_th.c │?? ├── msg.c │?? ├── time_tcp_client.c │?? └── time_tcp_server.c └── time_tcp_demo.tar.gz?
?
?
話不多說上服務(wù)器代碼:
#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> #include <pthread.h>/**************************************使用多進(jìn)程處理并發(fā)***************************************/ //處理多個進(jìn)程連接 //測試方法,運(yùn)行程序,并指定端口號8888 //在另一個終端上使用 // telnet 127.0.0.1 8888進(jìn)行連接 //127.0.0.1是沒有連接網(wǎng)絡(luò)時使用的本地回環(huán)ip地址/*聲明自定義函數(shù)*/ void sig_handler(int signo); void do_service(int fd); void *th_fn(void *arg); void out_fd(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創(chuàng)建socket*AF_INET IPV4*SOCK_STREAM 采用tcp協(xié)議*0 采用上面設(shè)置的協(xié)議類型*/sockfd = socket(AF_INET, SOCK_STREAM, 0); //使用默認(rèn)協(xié)議if(sockfd < 0){perror("socket error!");exit(1);}/**步驟2,:調(diào)用bind函數(shù)將socket和地址(包括 IP,port)進(jìn)行綁定**/struct sockaddr_in serveraddr; //聲明專用地址,需要的時候再轉(zhuǎn)換為通用地址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:調(diào)用listen函數(shù)啟動端口監(jiān)聽*通知系統(tǒng)去接受來自客戶端的連接請求*listen 的第二個參數(shù)10是請求隊(duì)列長度,將接收到的客戶端連接請求放到對應(yīng)的隊(duì)列當(dāng)中*/if(listen(sockfd, 10) < 0) //10監(jiān)聽的隊(duì)列的上限{perror("listen error!");exit(1);}/**步驟4:調(diào)用accept函數(shù),從隊(duì)列中獲得* 一個客戶端請求連接,并返回新的sock文件描述符fd,* 因?yàn)閘isten能夠監(jiān)聽好多的連接請求,* 使用accept獲得一個連接使用* 若是accept沒有獲得客戶端連接,會進(jìn)行阻塞,直到獲得客戶端連接*/struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);//使用分離狀態(tài)的子線程, 需要設(shè)置子線程的分離屬性pthread_attr_t attr;pthread_attr_init(&attr);//設(shè)置分離屬性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);while(1){//使用循環(huán)就能夠在斷開與一個客戶端連接之后,在連接下一個客戶端連接//調(diào)用accept就是在客戶端獲得一個連接//主控線程負(fù)責(zé)調(diào)用accept函數(shù)獲得客戶端的連接int fd = accept(sockfd, NULL, NULL);if(fd < 0){perror("accept error!");continue;}/**步驟5: 啟動子線程調(diào)用IO函數(shù)(read/write)和連接的客戶端進(jìn)行雙向通信*///以分離狀態(tài)啟動的子線程在結(jié)束的時候會自動的釋放自己占用的資源,不用主線程在使用join函數(shù)pthread_t th;int err;//將套接字的文件描述符傳進(jìn)線程創(chuàng)建函數(shù)if((err = pthread_create(&th, &attr, th_fn, (void *)fd)) != 0){perror("pthread create error!");} pthread_attr_destroy(&attr);}return 0; }void sig_handler(int signo) {if(signo == SIGINT){printf("server close\n");/*步驟6:關(guān)閉socket*/close(sockfd);exit(1);}/* //登記SIGCHLD信號 只有子進(jìn)程結(jié)束才會出現(xiàn),使用線程時去掉if(signo == SIGCHLD){printf("child process deaded...\n");wait(NULL); //會自動回收子進(jìn)程 wait函數(shù)中填入0說明對于回收的子進(jìn)程不關(guān)心只是去回收子進(jìn)程}*/}void do_service(int fd) {/*和客戶端雙向通訊,進(jìn)行讀寫操作*/char buff[512];while(1){memset(buff, 0, sizeof(buff));size_t size;if((size = read_msg(fd, buff, sizeof(buff))) < 0){perror("protocal error!");break;}else if (size == 0){//數(shù)據(jù)全部讀取完畢或者對方客戶端掛掉就會返回0break;}else{printf("%s\n", buff);if(write_msg(fd, buff, sizeof(buff)) < 0){if(errno == EPIPE)//若是對方已經(jīng)關(guān)閉就直接跳出{perror("write_msg error!");break;}}}} }void *th_fn(void *arg) {int fd = (int)arg;do_service(fd);out_fd(fd); //服務(wù)完畢之后輸出客戶端的一些信息close(fd);return (void *)0;} void out_fd(int fd) {struct sockaddr_in addr;socklen_t len = sizeof(addr);//從fd中獲得連接的客戶端的相關(guān)信息,把信息放進(jìn)sockaddr_in的結(jié)構(gòu)體addr中if(getpeername(fd, (struct sockaddr *)&addr, &len) < 0){perror("getpeername error!");return ;}char ip[16];memset(ip, 0, sizeof(ip));int port = ntohs(addr.sin_port);inet_ntop(AF_INET, &addr.sin_addr.s_addr, ip, sizeof(ip));printf("%16s(%5d) closed!\n", ip, port);}客戶端代碼:
#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:創(chuàng)建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地址轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序填入 serveraddr中*/inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);/**步驟2: 客戶端調(diào)用connect函數(shù)連接到服務(wù)器端*/if(connect(sockfd, (struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0){perror("connect error!");exit(1);}/*步驟3:調(diào)用IO函數(shù),read和write和服務(wù)器進(jìn)行雙向通信*/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; //就算寫出錯也結(jié)合運(yùn)行 進(jìn)行下次的發(fā)送}else {//客戶端發(fā)送成功if(read_msg(sockfd, buff, sizeof(buff)) < 0){perror("read msg error!");continue;}else{printf("%s\n",buff);}}}/*步驟4:關(guān)閉socket套接字*/close(sockfd);return 0; }Makefile文件;
.PHONY : all .PHONY : cleanall :bin/echo_tcp_server_th bin/echo_tcp_clientbin/echo_tcp_server_th : echo_tcp_server_thmv echo_tcp_server_th bin/echo_tcp_server_thbin/echo_tcp_client : echo_tcp_clientmv echo_tcp_client bin/echo_tcp_clientecho_tcp_server_th : src/echo_tcp_server_th.c obj/msg.ogcc -o echo_tcp_server_th -Iinclude src/echo_tcp_server_th.c obj/msg.o -lpthreadecho_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.cclean:-rm bin/* obj/*?
?
?
?
?
?
?
?
?
?
?
?
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的socket中使用多线程创建并发服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: getpeername函数与getsoc
- 下一篇: 作者:曹玉社(1990-),男,国防科学