linux 網絡編程:使用兩線程實現socket同時收發數據

http://blog.csdn.net/li_wen01/article/details/52665505

工作中最近有使用到socket 向客戶端同時發送和接收數據,因為是嵌入式linux設備,且要求只能同時一個客戶端連接該端口。考慮到節省系統資源,只創建了兩個線程分別實現服務端的收發數據。下面直接上代碼,該代碼為在PC機上程序,已作詳細注釋。

server.c

[objc]?view plaincopy
print?
  1. #include<stdio.h>??
  2. #include<stdlib.h>??
  3. #include<string.h>??
  4. #include<errno.h>??
  5. #include<sys/types.h>??
  6. #include<sys/socket.h>??
  7. #include<netinet/in.h>??
  8. #include<termios.h>??
  9. #include<sys/types.h>?????
  10. #include<sys/stat.h>??????
  11. #include<fcntl.h>??
  12. #include<unistd.h>??
  13. #include<sys/ioctl.h>??
  14. #include<signal.h>??
  15. ??
  16. #define?MAXLINE?256??
  17. #define?PORT????6666??
  18. int?listenfd;??
  19. int?connfd;??
  20. pthread_t?read_id,?write_id;??
  21. ??
  22. /*?
  23. linux?ctrl?+?C?會產生?SIGINT信號?
  24. 接收到SIGINT?信號進入該函數?
  25. */??
  26. void?stop(int?signo)??
  27. {??
  28. ????printf("stop\n");??
  29. ????close(connfd);????
  30. ????close(listenfd);??
  31. ?????_exit(0);??
  32. }??
  33. ??
  34. /*?
  35. 當客戶端斷開連接的時候,?
  36. 在服務端socket?send進程可以收到收到信號SIGPIPE,?
  37. 收到SIGPIPE信號進入該函數結束創建的線程。?
  38. */??
  39. void?signal_pipe(int?signo)??
  40. {??
  41. ????pthread_kill(read_id,SIGQUIT);//向read線程發送SIGQUIT??
  42. ????pthread_join(read_id,NULL);???//阻塞線程運行,直到read?線程退出。??
  43. ??????
  44. ????close(connfd);????????????????//關閉連接??
  45. ????printf("read?pthread?out?\n");??
  46. ??????
  47. ????pthread_exit(0);??????????????//結束write?線程??
  48. }??
  49. ??
  50. /*?
  51. read?線程接收到SIGQUIT信號,?
  52. 執行線程退出操作?
  53. */??
  54. void?pthread_out(int?signo)??
  55. {??
  56. ????pthread_exit(0);??
  57. }??
  58. ??
  59. /*?
  60. read?線程執行函數?
  61. */??
  62. void*?read_func(void*?arg)??
  63. {??
  64. ????char?readbuff[MAXLINE];??
  65. ????int?n?=?0;??
  66. ????int?fd;??
  67. ??
  68. ????fd?=?*(int*)arg;????/*main?主進程傳遞過來的連接文件描述符*/??
  69. ????memset(&readbuff,0,sizeof(readbuff));??
  70. ??
  71. ????signal(SIGQUIT,pthread_out);?/*?注冊SIGQUIT?信號*/???
  72. ????while(1)??
  73. ????{??
  74. ????????n?=?recv(fd,?readbuff,?MAXLINE,?0);??/*recv?在這里是阻塞運行*/??
  75. ????????if(n?>?0)??
  76. ????????{??
  77. ????????????printf("server?recv?data:?%s?\n",readbuff);??
  78. ????????}??
  79. ????};??
  80. }??
  81. /*?
  82. write?線程執行函數?
  83. */??
  84. void*?write_func(void*?arg)??
  85. {??
  86. ????char?writebuff[MAXLINE];??
  87. ????char*?write?=?"I?am?server";??
  88. ????unsigned?char?i?=?0;??
  89. ????int?num?=?0;??
  90. ????int?fd;??
  91. ??
  92. ????fd?=?*(int*)arg;??
  93. ????memset(&writebuff,0,sizeof(writebuff));??
  94. ??????
  95. ????signal(SIGPIPE,signal_pipe);?/*?注冊?SIGPIPE信號?*/??
  96. ????while(1)??
  97. ????{??
  98. ????????sleep(1);??
  99. ????????send(fd,write,strlen(write)+1,0);/*向客戶端發送數據*/??
  100. ????}??
  101. }??
  102. ??
  103. int?main(int?argc,?char**?argv)??
  104. {??
  105. ????char?buff[MAXLINE];??
  106. ????int?num;??
  107. ????int?addrlen;??
  108. ????struct?sockaddr_in?server_addr;??/*服務器地址結構*/??
  109. ????struct?sockaddr_in?client_addr;??/*客戶端地址結構*/??
  110. ??
  111. ????if((listenfd?=?socket(AF_INET,SOCK_STREAM,0))?==?-1)/*建立一個流式套接字*/??
  112. ????{??
  113. ????????printf("create?socket?error:?%s(errno:?%d)\n",strerror(errno),errno);??
  114. ????????exit(0);??
  115. ????}??
  116. ??????
  117. ????/*設置服務端地址*/??
  118. ????addrlen?=?sizeof(struct?sockaddr_in);??
  119. ????memset(&server_addr,?0,?addrlen);??
  120. ????server_addr.sin_family?=?AF_INET;????/*AF_INET表示?IPv4?Intern?協議*/??
  121. ????server_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);?/*INADDR_ANY?可以監聽任意IP?*/??
  122. ????server_addr.sin_port?=?htons(PORT);?/*設置端口*/??
  123. ??????
  124. ????/*綁定地址結構到套接字描述符*/??
  125. ????if(?bind(listenfd,?(struct?sockaddr*)&server_addr,?sizeof(server_addr))?==?-1)??
  126. ????{??
  127. ????????printf("bind?socket?error:?%s(errno:?%d)\n",strerror(errno),errno);??
  128. ????????exit(0);??
  129. ????}??
  130. ??
  131. ????/*設置監聽隊列,這里設置為1,表示只能同時處理一個客戶端的連接*/??
  132. ????if(?listen(listenfd,?1)?==?-1)??
  133. ????{??
  134. ????????printf("listen?socket?error:?%s(errno:?%d)\n",strerror(errno),errno);??
  135. ????????exit(0);??
  136. ????}??
  137. ??????
  138. ????signal(SIGINT,stop);?/*注冊SIGINT?信號*/??
  139. ????while(1)??
  140. ????{?????
  141. ????????printf("wait?client?accpt?\n");??
  142. ????????if(?(connfd?=?accept(listenfd,?(struct?sockaddr*)&client_addr,?&addrlen))?==?-1)/*接收客戶端的連接,這里會阻塞,直到有客戶端連接*/??
  143. ????????{??
  144. ????????????printf("accept?socket?error:?%s(errno:?%d)",strerror(errno),errno);??
  145. ????????????continue;??
  146. ????????}??
  147. ??????
  148. ????????if(pthread_create(&read_id,NULL,read_func,&connfd))/*創建?read?線程*/??
  149. ????????{??
  150. ????????????printf("pthread_create?read_func?err\n");??
  151. ????????}??
  152. ??
  153. ????????if(pthread_create(&write_id,NULL,write_func,&connfd))/*創建?write?線程*/??
  154. ????????{??
  155. ????????????printf("pthread_create?write_func?err\n");??
  156. ????????}??
  157. ??????????
  158. ????????pthread_join(write_id,NULL);?/*阻塞,直到write進程退出后才進行新的客戶端連接*/??
  159. ????????printf("write?pthread?out?\n");??
  160. ????}??
  161. }??
? ? 這里需要特別注意線程資源的回收,因為每次與客戶端建立連接,服務端都會創建連個線程,因此我們需要在客戶端斷開連接時回收線程資源。在這里有兩種方式來檢測客戶端是否已經斷開連接。第一:上面server.c服務端使用的,當客戶端斷開連接時,send 所在的進程會收到一個SIGPIPE信號,服務端收到SIGPIPE信號就進行線程退出操作。第二:recv函數在對方斷開連接它會返回參數0,根據該參數也可以知道客戶端已經斷開了連接。在下面你的client.c客戶端測試程序使用的就是這種方式。

? ? 這里還有一個問題,signal(SIGPIPE,signal_pipe)函數的注冊,我本來是把它放置在主線程main函數中,然后在signal_pipe 同時回收read 和 write線程,結果read進程可以正常回收,write線程卻回收失敗,具體原因也不清楚,求高人指點。于是我就是用SIGQUIT信號,在write線程收到SIGPIPE信號時,通知read線程退出,直到其退出再退出write線程。這樣就實現了系統資源的回收。

client客戶端測試程序:

[objc]?view plaincopy
print?
  1. #include<stdio.h>?????????
  2. #include<stdlib.h>??
  3. #include<string.h>??
  4. #include<errno.h>??
  5. #include<sys/types.h>??
  6. #include<sys/socket.h>??
  7. #include<netinet/in.h>??
  8. #include<termios.h>??
  9. #include<sys/types.h>?????
  10. #include<sys/stat.h>??????
  11. #include<fcntl.h>??
  12. #include<unistd.h>??
  13. #include<sys/ioctl.h>??
  14. #include<signal.h>??
  15. ??
  16. #define?MAXLINE?256??
  17. #define?PORT????6666??
  18. int?fd;??
  19. /*?
  20. linux?ctrl?+?C?會產生?SIGINT信號?
  21. 接收到SIGINT?信號進入該函數?
  22. */??
  23. void?stop(int?signo)??
  24. {??
  25. ????printf("client?stop\n");??
  26. ????close(fd);????
  27. ????_exit(0);??
  28. }??
  29. ??
  30. /*客戶端處理函數*/??
  31. void?client_process(void)??
  32. {??
  33. ????char?readbuff[MAXLINE];??
  34. ????char?writebuff[MAXLINE];??
  35. ????charchar?*?write?=?"I?am?client";??
  36. ????int?num?=?0;??
  37. ??????
  38. ????while(1)??
  39. ????{??
  40. ????????num?=?recv(fd,readbuff,MAXLINE,0);/*接收服務端的數據,recv在這里如果沒有數據會阻塞*/??
  41. ????????if(num?>?0)??
  42. ????????{??
  43. ????????????printf("client?read?data?:?%s?\n",readbuff);??
  44. ????????????send(fd,?write,?strlen(write)+1,?0);?/*接收到數據后再向服務端發送一個字符串*/??
  45. ????????}??
  46. ????????else?if(num?==?0)/*recv返回值為0?的時候表示服務端已經斷開了連接*/??
  47. ????????{??
  48. ????????????stop(1);?/*執行退出操作*/??
  49. ????????}??
  50. ????}??
  51. }??
  52. ??
  53. int?main(int?argc,?char**?argv)??
  54. {??
  55. ????struct?sockaddr_in?server_addr;??
  56. ????struct?sockaddr_in?client_addr;??
  57. ????int?ret;??
  58. ??
  59. ????fd?=?socket(AF_INET,SOCK_STREAM,0);/*建立流式套接字*/??
  60. ????if(fd?<?0)??
  61. ????{??
  62. ????????printf("clinet?socket?err?\n");??
  63. ????}??
  64. ??
  65. ????/*設置服務端地址*/??
  66. ????memset(&server_addr,0,sizeof(server_addr));??
  67. ????server_addr.sin_family?=?AF_INET;???????????????/*AF_INET表示?IPv4?Intern?協議*/??
  68. ????server_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);/*INADDR_ANY?可以監聽任意IP?*/??
  69. ????server_addr.sin_port?=?htons(PORT);?????????????/*設置端口*/??
  70. ??
  71. ????inet_pton(AF_INET,argv[1],&server_addr.sin_addr);/*將用戶輸入的字符串類型的IP地址轉為整型*/??
  72. ????connect(fd,(struct?sockaddr*)&server_addr,sizeof(server_addr));/*連接服務器*/??
  73. ??????
  74. ????signal(SIGINT,stop);????/*注冊SIGINT信號*/??
  75. ????client_process();???????/*進入處理函數*/??
  76. ??????
  77. ????close(fd);/*關閉文件*/??
  78. ????return?0;??
  79. }??
? ? 在客戶端是用recv 函數的返回值判斷服務端是否斷開連接而不是SIGPIPE信號,主要是在這程序中,我的實現是send函數在接收到服務端的數據才發送,所以如果在這里不會觸發SIGPIPE信號。如果要實現客戶端的獨立收發,可以模仿服務端的實現方式,這里不再現實。

下面是測試執行情況:

[objc]?view plaincopy
print?
  1. root@ubuntu:/home/share/Socket#?./server???
  2. wait?client?accpt???
  3. server?recv?data:?I?am?client???
  4. server?recv?data:?I?am?client???
  5. server?recv?data:?I?am?client???
  6. server?recv?data:?I?am?client???
  7. server?recv?data:?I?am?client???
  8. server?recv?data:?I?am?client???
  9. server?recv?data:?I?am?client???
  10. server?recv?data:?I?am?client???
  11. read?pthread?out???
  12. write?pthread?out???
  13. wait?client?accpt???
  14. server?recv?data:?I?am?client???
  15. server?recv?data:?I?am?client???
  16. server?recv?data:?I?am?client???
  17. read?pthread?out???
  18. write?pthread?out???
  19. wait?client?accpt??
  20. ??
  21. ??
  22. ??
  23. root@ubuntu:/home/share/Socket#?./client?127.0.0.1??
  24. client?read?data?:?I?am?server???
  25. client?read?data?:?I?am?server???
  26. client?read?data?:?I?am?server???
  27. client?read?data?:?I?am?server???
  28. client?read?data?:?I?am?server???
  29. client?read?data?:?I?am?server???
  30. client?read?data?:?I?am?server???
  31. client?read?data?:?I?am?server???
  32. ^Cclient?stop??
  33. root@ubuntu:/home/share/Socket#?./client?127.0.0.1??
  34. client?read?data?:?I?am?server???
  35. client?read?data?:?I?am?server???
  36. client?read?data?:?I?am?server???
  37. ^Cclient?stop??
  38. root@ubuntu:/home/share/Socket#???
? ? 執行服務端從程序等待客戶連接,執行客戶端程序與服務端建立連接,服務端每秒向客戶端發送字符串:I am server,客戶端接收到數據后向服務端發送字符串: I am client 。在client端按下ctrl + c 結束客戶端程序,服務端檢測到客戶端斷開連接后,關閉文件描述符,回收創建的read 和write 線程。

[objc]?view plaincopy
print?
  1. root@ubuntu:/home/share/Socket#?./server???
  2. wait?client?accpt???
  3. server?recv?data:?I?am?client???
  4. server?recv?data:?I?am?client???
  5. server?recv?data:?I?am?client???
  6. server?recv?data:?I?am?client???
  7. server?recv?data:?I?am?client???
  8. ^Cstop??
  9. root@ubuntu:/home/share/Socket#???
  10. ??
  11. root@ubuntu:/home/share/Socket#?./client?127.0.0.1??
  12. client?read?data?:?I?am?server???
  13. client?read?data?:?I?am?server???
  14. client?read?data?:?I?am?server???
  15. client?read?data?:?I?am?server???
  16. client?read?data?:?I?am?server???
  17. client?stop??
  18. root@ubuntu:/home/share/Socket#??
? ?在服務端按下ctlc + c 服務端檢測到SIGINT信號,關閉文件描述符結束程序,整個進程全部退出。客戶端檢測到服務端斷開連接后,執行退出操作。

這里實現的是只有一個客戶端同時連接時的處理方式,如果要處理多客戶端同時連接,使用這種方式好像并不合適,可以使用多進程的方式處理。




本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383937.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383937.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383937.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CF Gym102059 H. Fractions

題目要求找到給定區間的化簡后分子分母的和小于1000的數字的個數 我的想法是先找到所有的滿足要求的最簡分數(總數不超過1e6,而且遠小于),然后對詢問查找每個最簡分數出現的次數. #include<cstdio> #include<cstring> #include<algorithm> #include<cli…

C語言calloc()函數:分配內存空間并初始化

http://c.biancheng.net/cpp/html/134.html 頭文件&#xff1a;#include <stdlib.h> calloc() 函數用來動態地分配內存空間并初始化為 0&#xff0c;其原型為&#xff1a; void* calloc (size_t num, size_t size); calloc() 在內存中動態地分配 num 個長度為 siz…

CF Gym100917 C

要找到和為給定值的所有的等比數列. 1肯定是要特判一下. 我的想法是先找到所有等比為1的,等比為1就是將這個數分為相同的一些數,總共就是這個數的所有約數個數減一(有一個約數為1,題目要求至少分成兩個數). 對于其他的等比不為1 的,用等比數列的求和公式暴力一發就行了. #i…

多路轉接select1

高級IO 通常情況下所有的 IO 都可以分為兩步來完成, 第一步等待, 第二步數據搬遷, 為了提高 IO 效率通常所運用的方法就是減少等待的時間 舉個釣魚的例子 現在有五個人張三, 李四, 王五, 趙六, 錢七. 它們五個人來到湖邊來釣魚. 而它們五個人的釣魚方各不相同. 張三釣魚方法…

UVa11181

題目要求條件概率,用貝葉斯公式我們很容易得到我們需要求r個人買東西的概率和每個人買東西的條件下其他r-1個人買東西的概率.我們遞歸枚舉,每當枚舉到r個人買東西的時候,我們加入到r個人買東西的概率中(全概率公式),然后對于這r個人,除去自己買東西的概率就是其他r-1個人買東西…

Linux epoll模型

http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html 定義&#xff1a; epoll是Linux內核為處理大批句柄而作改進的poll&#xff0c;是Linux下多路復用IO接口select/poll的增強版本&#xff0c;它能顯著的減少程序在大量并發連接中只有少量活躍的情況下的系統CPU利…

UVa11572

書上把這種問題叫做滑動窗口問題. 我的想法是先進行離散化,然后用一個數組記錄元素出現的位置,如果判斷某個元素已經出現,就將左端點移到上次出現的位置的后面.每次出現重復元素的時候判斷一下答案.我覺得這樣的復雜度是最低的. #include<cstdio> #include<cstring&…

Linux IO模式及 select、poll、epoll詳解

https://segmentfault.com/a/1190000003063859 同步IO和異步IO&#xff0c;阻塞IO和非阻塞IO分別是什么&#xff0c;到底有什么區別&#xff1f;不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上下文。 本文討論的背景是Linux環境下的network IO。一 概念…

mysql思維導圖

后期會不斷進行更新

CF Gym 101630 B Box

題目的意思大概就是給一個長方體的長寬高,問他能不能用一個w*h的紙剪出來,就是說展開圖的長寬能不能比給定的小. 題目給了11中展開圖的拓撲結構,我覺得這個很關鍵,要是題目沒有給這個我可能想不到那么全面,不過題目已經給了我就分析那11個圖形,發現展開圖的長寬大概分為三類 …

C++第一節課

C數據類型 幾個概念 命名空間是C標準庫引入的,其中命名空間可以解決變量沖突問題,當出現局部變量和全局變量同名的時候, 局部變量優先被訪問.同時命名空間的格式如同一下代碼 namespace name1 { int a 0; }namespace name2 { int a 2; } 注意C中的所有組件都是在一個叫做s…

【C/C++】關鍵字static

http://blog.csdn.net/woxiaohahaa/article/details/51014224 參考自&#xff1a;http://www.cnblogs.com/biyeymyhjob/archive/2012/07/19/2598815.html &#xff08;華山大師兄&#xff09; 這里我們只討論了C語言的static 首先我們回顧一下各種變量在內存中的位置&#xff1…

HDU5391威爾遜定理

威爾遜定理 當且僅當p為素數,p | (p-1)!1 若p為合數,則pa*b;如果a!b,那么p|(p-1)!, 如果ab,如果p為4,那么p|(p-1)!2,如果p大于4,那么sqrt和sqrt(2q)肯定屬于(p-1)!中,可以整除 #include<cstdio> #include<cstring> #include<algorithm> #include<climit…

C++的基本認識

簡單介紹C 語言特點 支持數據封裝和數據隱藏 在C中&#xff0c;類是支持數據封裝的工具&#xff0c;對象則是數據封裝的實現。C通過建立用戶定義類支持數據封裝和數據隱藏。 在面向對象的程序設計中&#xff0c;將數據和對該數據進行合法操作的函數封裝在一起作為一個類的定…

OD 投籃大賽

/*** 題目描述* 你現在是一場采用特殊賽制投籃大賽的記錄員。這場比賽由若干回合組成&#xff0c;過去幾回合的得分可能會影響以后幾回合的得分。* 比賽開始時&#xff0c;記錄時空白的。你會得到一個記錄操作的字符串列表aops&#xff0c;其中ops[i]是你需要記錄的第i項操作&a…

IO多路復用之epoll總結

http://www.cnblogs.com/Anker/p/3263780.html 1、基本知識 epoll是在2.6內核中提出的&#xff0c;是之前的select和poll的增強版本。相對于select和poll來說&#xff0c;epoll更加靈活&#xff0c;沒有描述符限制。epoll使用一個文件描述符管理多個描述符&#xff0c;將用戶關…

2018南京區域賽 J-Prime Game

完全沒有頭緒 聽完隊友講的我還是楞了好半天菜慢慢理解.我好菜啊 首先要弄懂題目的意思,轉換一下題意就是求每個素因子出現區間的次數.區間長度最短為1.我們分析,第一個數的因子會影響1* n個區間(暫時不考慮重復),第二個數的因子會影響2 * (n-1)個區間,以此類推.因此我們只需要…

3_V1-類和對象 -- 默認成員函數

定義一個日期類 #include <iostream> #include <assert.h> using namespace std;class Date { public:void Display(); private:int _year;int _month;int _day; }; 注意: 在定義一個類的時候往往會將其成員變量定義為私有,成員函數定義為公有.這是為了達到軟件…

C++ 類模板二(類模版與友元函數)

http://www.cnblogs.com/zhanggaofeng/p/5661829.html //類模版與友元函數 #include<iostream> using namespace std;template<typename T> class Complex{ public:Complex(T a,T b);void Print() const//const修飾的是this指針{cout << this->Real <&…

HDU - 2973威爾遜定理

核心問題就是那個等式 我們觀察到等式可以寫成(n-1)!-1/n-[(n-1)!/n]的形式&#xff0c;這樣就應該聯想到威爾遜定理了。 回顧一下威爾遜定理的內容&#xff1a;當且僅當n為素數的時候n|(n-1)!-1&#xff0c;n為合數且大于4的時候n|(n-1)!【參見威爾遜定理的證明】 對于這個…