生活随笔
收集整理的這篇文章主要介紹了
linux 网络编程:使用两线程实现socket同时收发数据
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
http://blog.csdn.net/li_wen01/article/details/52665505
工作中最近有使用到socket 向客戶端同時發送和接收數據,因為是嵌入式linux設備,且要求只能同時一個客戶端連接該端口??紤]到節省系統資源,只創建了兩個線程分別實現服務端的收發數據。下面直接上代碼,該代碼為在PC機上程序,已作詳細注釋。
server.c
[objc] ?view plaincopy print?
#include<stdio.h> ?? #include<stdlib.h> ?? #include<string.h> ?? #include<errno.h> ?? #include<sys/types.h> ?? #include<sys/socket.h> ?? #include<netinet/in.h> ?? #include<termios.h> ?? #include<sys/types.h>??? ?? #include<sys/stat.h>???? ?? #include<fcntl.h> ?? #include<unistd.h> ?? #include<sys/ioctl.h> ?? #include<signal.h> ?? ?? #define?MAXLINE?256 ?? #define?PORT????6666 ?? int ?listenfd;?? int ?connfd;?? pthread_t?read_id,?write_id;?? ?? ? ? ? ?? void ?stop( int ?signo)?? {?? ????printf("stop\n" );?? ????close(connfd);???? ????close(listenfd);?? ?????_exit(0 );?? }?? ?? ? ? ? ? ?? void ?signal_pipe( int ?signo)?? {?? ????pthread_kill(read_id,SIGQUIT);?? ????pthread_join(read_id,NULL );??? ?? ?????? ????close(connfd);?????????????????? ????printf("read?pthread?out?\n" );?? ?????? ????pthread_exit(0 );?????????????? ?? }?? ?? ? ? ? ?? void ?pthread_out( int ?signo)?? {?? ????pthread_exit(0 );?? }?? ?? ? ? ?? void *?read_func( void *?arg)?? {?? ????char ?readbuff[MAXLINE];?? ????int ?n?=? 0 ;?? ????int ?fd;?? ?? ????fd?=?*(int *)arg;???? ?? ????memset(&readbuff,0 , sizeof (readbuff));?? ?? ????signal(SIGQUIT,pthread_out);???? ????while ( 1 )?? ????{?? ????????n?=?recv(fd,?readbuff,?MAXLINE,?0 );?? ?? ????????if (n?>? 0 )?? ????????{?? ????????????printf("server?recv?data:?%s?\n" ,readbuff);?? ????????}?? ????};?? }?? ? ? ?? void *?write_func( void *?arg)?? {?? ????char ?writebuff[MAXLINE];?? ????char *?write?=? "I?am?server" ;?? ????unsigned?char ?i?=? 0 ;?? ????int ?num?=? 0 ;?? ????int ?fd;?? ?? ????fd?=?*(int *)arg;?? ????memset(&writebuff,0 , sizeof (writebuff));?? ?????? ????signal(SIGPIPE,signal_pipe);??? ????while ( 1 )?? ????{?? ????????sleep(1 );?? ????????send(fd,write,strlen(write)+1 , 0 ); ?? ????}?? }?? ?? int ?main( int ?argc,? char **?argv)?? {?? ????char ?buff[MAXLINE];?? ????int ?num;?? ????int ?addrlen;?? ????struct ?sockaddr_in?server_addr;?? ?? ????struct ?sockaddr_in?client_addr;?? ?? ?? ????if ((listenfd?=?socket(AF_INET,SOCK_STREAM, 0 ))?==?- 1 ) ?? ????{?? ????????printf("create?socket?error:?%s(errno:?%d)\n" ,strerror(errno),errno);?? ????????exit(0 );?? ????}?? ?????? ?????? ????addrlen?=?sizeof ( struct ?sockaddr_in);?? ????memset(&server_addr,?0 ,?addrlen);?? ????server_addr.sin_family ?=?AF_INET;???? ?? ????server_addr.sin_addr .s_addr ?=?htonl(INADDR_ANY);? ?? ????server_addr.sin_port ?=?htons(PORT);? ?? ?????? ?????? ????if (?bind(listenfd,?( struct ?sockaddr*)&server_addr,? sizeof (server_addr))?==?- 1 )?? ????{?? ????????printf("bind?socket?error:?%s(errno:?%d)\n" ,strerror(errno),errno);?? ????????exit(0 );?? ????}?? ?? ?????? ????if (?listen(listenfd,? 1 )?==?- 1 )?? ????{?? ????????printf("listen?socket?error:?%s(errno:?%d)\n" ,strerror(errno),errno);?? ????????exit(0 );?? ????}?? ?????? ????signal(SIGINT,stop);??? ????while ( 1 )?? ????{????? ????????printf("wait?client?accpt?\n" );?? ????????if (?(connfd?=?accept(listenfd,?( struct ?sockaddr*)&client_addr,?&addrlen))?==?- 1 ) ?? ????????{?? ????????????printf("accept?socket?error:?%s(errno:?%d)" ,strerror(errno),errno);?? ????????????continue ;?? ????????}?? ?????? ????????if (pthread_create(&read_id, NULL ,read_func,&connfd)) ?? ????????{?? ????????????printf("pthread_create?read_func?err\n" );?? ????????}?? ?? ????????if (pthread_create(&write_id, NULL ,write_func,&connfd)) ?? ????????{?? ????????????printf("pthread_create?write_func?err\n" );?? ????????}?? ?????????? ????????pthread_join(write_id,NULL );? ?? ????????printf("write?pthread?out?\n" );?? ????}?? }??
? ? 這里需要特別注意線程資源的回收,因為每次與客戶端建立連接,服務端都會創建連個線程,因此我們需要在客戶端斷開連接時回收線程資源。在這里有兩種方式來檢測客戶端是否已經斷開連接。第一:上面server.c服務端使用的,當客戶端斷開連接時,send 所在的進程會收到一個SIGPIPE信號,服務端收到SIGPIPE信號就進行線程退出操作。第二:recv函數在對方斷開連接它會返回參數0,根據該參數也可以知道客戶端已經斷開了連接。在下面你的client.c客戶端測試程序使用的就是這種方式。
? ? 這里還有一個問題,signal(SIGPIPE,signal_pipe)函數的注冊,我本來是把它放置在主線程main函數中,然后在signal_pipe 同時回收read 和 write線程,結果read進程可以正?;厥?#xff0c;write線程卻回收失敗,具體原因也不清楚,求高人指點。于是我就是用SIGQUIT信號,在write線程收到SIGPIPE信號時,通知read線程退出,直到其退出再退出write線程。這樣就實現了系統資源的回收。
client客戶端測試程序:
[objc] ?view plaincopy print?
#include<stdio.h>??????? ?? #include<stdlib.h> ?? #include<string.h> ?? #include<errno.h> ?? #include<sys/types.h> ?? #include<sys/socket.h> ?? #include<netinet/in.h> ?? #include<termios.h> ?? #include<sys/types.h>??? ?? #include<sys/stat.h>???? ?? #include<fcntl.h> ?? #include<unistd.h> ?? #include<sys/ioctl.h> ?? #include<signal.h> ?? ?? #define?MAXLINE?256 ?? #define?PORT????6666 ?? int ?fd;?? ? ? ? ?? void ?stop( int ?signo)?? {?? ????printf("client?stop\n" );?? ????close(fd);???? ????_exit(0 );?? }?? ?? ?? void ?client_process( void )?? {?? ????char ?readbuff[MAXLINE];?? ????char ?writebuff[MAXLINE];?? ????char char ?*?write?=? "I?am?client" ;?? ????int ?num?=? 0 ;?? ?????? ????while ( 1 )?? ????{?? ????????num?=?recv(fd,readbuff,MAXLINE,0 ); ?? ????????if (num?>? 0 )?? ????????{?? ????????????printf("client?read?data?:?%s?\n" ,readbuff);?? ????????????send(fd,?write,?strlen(write)+1 ,? 0 );? ?? ????????}?? ????????else ? if (num?==? 0 ) ?? ????????{?? ????????????stop(1 );? ?? ????????}?? ????}?? }?? ?? int ?main( int ?argc,? char **?argv)?? {?? ????struct ?sockaddr_in?server_addr;?? ????struct ?sockaddr_in?client_addr;?? ????int ?ret;?? ?? ????fd?=?socket(AF_INET,SOCK_STREAM,0 ); ?? ????if (fd?<? 0 )?? ????{?? ????????printf("clinet?socket?err?\n" );?? ????}?? ?? ?????? ????memset(&server_addr,0 , sizeof (server_addr));?? ????server_addr.sin_family ?=?AF_INET;??????????????? ?? ????server_addr.sin_addr .s_addr ?=?htonl(INADDR_ANY); ?? ????server_addr.sin_port ?=?htons(PORT);????????????? ?? ?? ????inet_pton(AF_INET,argv[1 ],&server_addr .sin_addr ); ?? ????connect(fd,(struct ?sockaddr*)&server_addr, sizeof (server_addr)); ?? ?????? ????signal(SIGINT,stop);?????? ????client_process();????????? ?????? ????close(fd);?? ????return ? 0 ;?? }??
? ? 在客戶端是用recv 函數的返回值判斷服務端是否斷開連接而不是SIGPIPE信號,主要是在這程序中,我的實現是send函數在接收到服務端的數據才發送,所以如果在這里不會觸發SIGPIPE信號。如果要實現客戶端的獨立收發,可以模仿服務端的實現方式,這里不再現實。
下面是測試執行情況:
[objc] ?view plaincopy print?
root @ubuntu :/home/share/Socket#?./server??? wait?client?accpt??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? read?pthread?out??? write?pthread?out??? wait?client?accpt??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? read?pthread?out??? write?pthread?out??? wait?client?accpt?? ?? ?? ?? root@ubuntu :/home/share/Socket#?./client? 1 2 7 .0 .0 .1 ?? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? ^Cclient?stop?? root@ubuntu :/home/share/Socket#?./client? 1 2 7 .0 .0 .1 ?? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? ^Cclient?stop?? root@ubuntu :/home/share/Socket#???
? ? 執行服務端從程序等待客戶連接,執行客戶端程序與服務端建立連接,服務端每秒向客戶端發送字符串:I am server,客戶端接收到數據后向服務端發送字符串: I am client 。在client端按下ctrl + c 結束客戶端程序,服務端檢測到客戶端斷開連接后,關閉文件描述符,回收創建的read 和write 線程。
[objc] ?view plaincopy print?
root @ubuntu :/home/share/Socket#?./server??? wait?client?accpt??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? server?recv?data :?I?am?client??? ^Cstop?? root@ubuntu :/home/share/Socket#??? ?? root@ubuntu :/home/share/Socket#?./client? 1 2 7 .0 .0 .1 ?? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?read?data?:?I?am?server??? client?stop?? root@ubuntu :/home/share/Socket#??
? ?在服務端按下ctlc + c 服務端檢測到SIGINT信號,關閉文件描述符結束程序,整個進程全部退出??蛻舳藱z測到服務端斷開連接后,執行退出操作。
這里實現的是只有一個客戶端同時連接時的處理方式,如果要處理多客戶端同時連接,使用這種方式好像并不合適,可以使用多進程的方式處理。
創作挑戰賽 新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔 為你收集整理的linux 网络编程:使用两线程实现socket同时收发数据 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。