Linux程序設計之套接字: TCP網絡服務器源碼

    大家都知道各類網絡服務器程序的編寫步驟,並且都知道網絡服務器就兩大類:循環服務和併發服務。這裏附上源代碼來個小結吧。

    下面是一個TCP循環服務器源程序,因爲用fork()進行多進程服務,父進程負責監聽和連接,子進程負責與客戶端進行通信,所以這種服務現實中也有用。源代碼如下:

  1. /*----------------------源代碼開始--------------------------------------------*/ 
  2. #include <stdio.h> 
  3. #include <stdlib.h> 
  4. #include <errno.h> 
  5. #include <string.h> 
  6. #include <sys/types.h> 
  7. #include <netinet/in.h> 
  8. #include <sys/socket.h> 
  9. #include <sys/wait.h> 
  10. /********************************************************************* 
  11. *filename: cycletcpserver.c 
  12. *purpose: 循環tcp服務端程序 
  13. *tidied by: zhoulifa([email protected]) 周立發(http://zhoulifa.9999mb.com) 
  14. Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言 
  15. *date time:2006-07-04 22:00:00 
  16. *Note: 任何人可以任意複製代碼並運用這些文檔,當然包括你的商業用途 
  17. * 但請遵循GPL 
  18. *Thanks to: Google.com 
  19. *********************************************************************/ 
  20. int main(int argc, char ** argv) 
  21.     int sockfd,new_fd; /* 監聽socket: sock_fd,數據傳輸socket: new_fd */ 
  22.     struct sockaddr_in my_addr; /* 本機地址信息 */ 
  23.     struct sockaddr_in their_addr; /* 客戶地址信息 */ 
  24.     unsigned int sin_size, myport, lisnum; 
  25.  
  26.     if(argv[1])  myport = atoi(argv[1]); 
  27.     else myport = 7838; 
  28.  
  29.     if(argv[2])  lisnum = atoi(argv[2]); 
  30.     else lisnum = 2; 
  31.  
  32.     if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { 
  33.         perror("socket"); 
  34.         exit(1); 
  35.     } 
  36.     my_addr.sin_family=PF_INET; 
  37.     my_addr.sin_port=htons(myport); 
  38.     my_addr.sin_addr.s_addr = INADDR_ANY; 
  39.     bzero(&(my_addr.sin_zero), 0); 
  40.     if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) { 
  41.         perror("bind"); 
  42.         exit(1); 
  43.     } 
  44.  
  45.     if (listen(sockfd, lisnum) == -1) { 
  46.         perror("listen"); 
  47.         exit(1); 
  48.     } 
  49.     while(1) { 
  50.         sin_size = sizeof(struct sockaddr_in); 
  51.         if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) { 
  52.             perror("accept"); 
  53.             continue
  54.         } 
  55.         printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr)); 
  56.         if (!fork()) { /* 子進程代碼段 */ 
  57.             if (send(new_fd, "Hello, world!\n", 14, 0) == -1) { 
  58.                 perror("send"); 
  59.                 close(new_fd); 
  60.                 exit(0); 
  61.             } 
  62.         } 
  63.         close(new_fd); /*父進程不再需要該socket*/ 
  64.         waitpid(-1,NULL,WNOHANG);/*等待子進程結束,清除子進程所佔用資源*/ 
  65.     } 
  66. /*----------------------源代碼結束--------------------------------------------*/

    下面是一個演示I/O多路複用的源程序,是一個端口轉發程序,但它的用處相當大,實際應用中的各類代理軟件或端口映射軟件都是基於這樣的代碼的,比如Windows下的WinGate、WinProxy等都是在此基礎上實現的。源代碼如下:

  1. /*----------------------源代碼開始--------------------------------------------*/ 
  2. #include <stdlib.h> 
  3. #include <stdio.h> 
  4. #include <unistd.h> 
  5. #include <sys/time.h> 
  6. #include <sys/types.h> 
  7. #include <string.h> 
  8. #include <signal.h> 
  9. #include <sys/socket.h> 
  10. #include <netinet/in.h> 
  11. #include <arpa/inet.h> 
  12. #include <errno.h> 
  13.  
  14. static int forward_port; 
  15.  
  16. #undef max 
  17. #define max(x,y) ((x) > (y) ? (x) : (y)) 
  18.  
  19. /*************************關於本文檔************************************ 
  20. *filename: tcpforwardport.c 
  21. *purpose: 演示了select的用法,這是一個極好的代理軟件核心,專門作端口映射用 
  22. *tidied by: zhoulifa([email protected]) 周立發(http://zhoulifa.9999mb.com) 
  23. Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言 
  24. *date time:2006-07-05 19:00:00 
  25. *Note: 任何人可以任意複製代碼並運用這些文檔,當然包括你的商業用途 
  26. * 但請遵循GPL 
  27. *Thanks to: Paul Sheer 感謝Paul Sheer在select_tut的man手冊裏提供了這份源代碼 
  28. *Hope:希望越來越多的人貢獻自己的力量,爲科學技術發展出力 
  29. *********************************************************************/ 
  30.  
  31. static int listen_socket (int listen_port) { 
  32.     struct sockaddr_in a; 
  33.     int s; 
  34.     int yes; 
  35.     if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { 
  36.         perror ("socket"); 
  37.         return -1; 
  38.     } 
  39.     yes = 1; 
  40.     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 
  41. 0) { 
  42.         perror ("setsockopt"); 
  43.         close (s); 
  44.         return -1; 
  45.     } 
  46.     memset (&a, 0, sizeof (a)); 
  47.     a.sin_port = htons (listen_port); 
  48.     a.sin_family = AF_INET; 
  49.     if (bind(s, (struct sockaddr *) &a, sizeof (a)) < 0) { 
  50.         perror ("bind"); 
  51.         close (s); 
  52.         return -1; 
  53.     } 
  54.     printf ("accepting connections on port %d\n", (int) listen_port); 
  55.     listen (s, 10); 
  56.     return s; 
  57.  
  58. static int connect_socket (int connect_port, char *address) { 
  59.     struct sockaddr_in a; 
  60.     int s; 
  61.     if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { 
  62.         perror ("socket"); 
  63.         close (s); 
  64.         return -1; 
  65.     } 
  66.  
  67.     memset (&a, 0, sizeof (a)); 
  68.     a.sin_port = htons (connect_port); 
  69.     a.sin_family = AF_INET; 
  70.  
  71.     if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) { 
  72.         perror ("bad IP address format"); 
  73.         close (s); 
  74.         return -1; 
  75.     } 
  76.  
  77.     if (connect(s, (struct sockaddr *) &a, sizeof (a)) < 0) { 
  78.         perror ("connect()"); 
  79.         shutdown (s, SHUT_RDWR); 
  80.         close (s); 
  81.         return -1; 
  82.     } 
  83.     return s; 
  84.  
  85. #define SHUT_FD1 { \ 
  86.     if (fd1 >= 0) {   \ 
  87.         shutdown (fd1, SHUT_RDWR);  \ 
  88.         close (fd1);  \ 
  89.         fd1 = -1;     \ 
  90.     }   \ 
  91.  
  92. #define SHUT_FD2 { \ 
  93.     if (fd2 >= 0) {   \ 
  94.         shutdown (fd2, SHUT_RDWR);  \ 
  95.         close (fd2);  \ 
  96.         fd2 = -1;     \ 
  97.     }   \ 
  98.  
  99. #define BUF_SIZE 1024 
  100.  
  101. int main (int argc, char **argv) { 
  102.     int h; 
  103.     int fd1 = -1, fd2 = -1; 
  104.     char buf1[BUF_SIZE], buf2[BUF_SIZE]; 
  105.     int buf1_avail, buf1_written; 
  106.     int buf2_avail, buf2_written; 
  107.  
  108.     if (argc != 4) { 
  109.         fprintf (stderr, "Usage\n\tfwd   \n"); 
  110.         exit (1); 
  111.     } 
  112.  
  113.     signal (SIGPIPE, SIG_IGN); 
  114.  
  115.     forward_port = atoi (argv[2]); 
  116.  
  117.     /*建立監聽socket*/ 
  118.     h = listen_socket (atoi (argv[1])); 
  119.     if (h < 0) exit (1); 
  120.  
  121.     for (;;) { 
  122.         int r, nfds = 0; 
  123.         fd_set rd, wr, er; 
  124.         FD_ZERO (&rd); 
  125.         FD_ZERO (&wr); 
  126.         FD_ZERO (&er); 
  127.         FD_SET (h, &rd); 
  128.  
  129.         /*把監聽socket和可讀socket三個一起放入select的可讀句柄列表裏*/ 
  130.         nfds = max (nfds, h); 
  131.         if (fd1 > 0 && buf1_avail < BUF_SIZE) { 
  132.             FD_SET (fd1, &rd); 
  133.             nfds = max (nfds, fd1); 
  134.         } 
  135.         if (fd2 > 0 && buf2_avail < BUF_SIZE) { 
  136.             FD_SET (fd2, &rd); 
  137.             nfds = max (nfds, fd2); 
  138.         } 
  139.  
  140.         /*把可寫socket兩個一起放入select的可寫句柄列表裏*/ 
  141.         if (fd1 > 0 && buf2_avail - buf2_written > 0) { 
  142.             FD_SET (fd1, &wr); 
  143.             nfds = max (nfds, fd1); 
  144.         } 
  145.         if (fd2 > 0 && buf1_avail - buf1_written > 0) { 
  146.             FD_SET (fd2, &wr); 
  147.             nfds = max (nfds, fd2); 
  148.         } 
  149.  
  150.         /*把有異常數據的socket兩個一起放入select的異常句柄列表裏*/ 
  151.         if (fd1 > 0) { 
  152.             FD_SET (fd1, &er); 
  153.             nfds = max (nfds, fd1); 
  154.         } 
  155.         if (fd2 > 0) { 
  156.             FD_SET (fd2, &er); 
  157.             nfds = max (nfds, fd2); 
  158.         } 
  159.  
  160.         /*開始select*/ 
  161.         r = select (nfds + 1, &rd, &wr, &er, NULL); 
  162.  
  163.         if (r == -1 && errno == EINTR) continue
  164.         if (r < 0) { 
  165.             perror ("select()"); 
  166.             exit (1); 
  167.         } 
  168.  
  169.         /*處理新連接*/ 
  170.         if (FD_ISSET (h, &rd)) { 
  171.             unsigned int l; 
  172.             struct sockaddr_in client_address; 
  173.             memset (&client_address, 0, l = sizeof (client_address)); 
  174.             r = accept (h, (struct sockaddr *)&client_address, &l); 
  175.             if (r < 0) { 
  176.                 perror ("accept()"); 
  177.             } else { 
  178.                 /*關閉原有連接,把新連接作爲fd1,同時連接新的目標fd2*/ 
  179.                 SHUT_FD1; 
  180.                 SHUT_FD2; 
  181.                 buf1_avail = buf1_written = 0; 
  182.                 buf2_avail = buf2_written = 0; 
  183.                 fd1 = r; 
  184.                 fd2 = connect_socket (forward_port, argv[3]); 
  185.                 if (fd2 < 0) { 
  186.                     SHUT_FD1; 
  187.                 } else 
  188.                     printf ("connect from %s\n", inet_ntoa(client_address.sin_addr)); 
  189.             } 
  190.         } 
  191.  
  192.         /* NB: read oob data before normal reads */ 
  193.         if (fd1 > 0) 
  194.         if (FD_ISSET (fd1, &er)) { 
  195.             char c; 
  196.             errno = 0; 
  197.             r = recv (fd1, &c, 1, MSG_OOB); 
  198.             if (r < 1) { 
  199.                 SHUT_FD1; 
  200.             } else 
  201.                 send (fd2, &c, 1, MSG_OOB); 
  202.         } 
  203.  
  204.         if (fd2 > 0) 
  205.         if (FD_ISSET (fd2, &er)) { 
  206.             char c; 
  207.             errno = 0; 
  208.             r = recv (fd2, &c, 1, MSG_OOB); 
  209.             if (r < 1) { 
  210.                 SHUT_FD1; 
  211.             } else 
  212.                 send (fd1, &c, 1, MSG_OOB); 
  213.         } 
  214.  
  215.         /* NB: read data from fd1 */ 
  216.         if (fd1 > 0) 
  217.         if (FD_ISSET (fd1, &rd)) { 
  218.             r = read (fd1, buf1 + buf1_avail, BUF_SIZE - buf1_avail); 
  219.             if (r < 1) { 
  220.                 SHUT_FD1; 
  221.             } else 
  222.                 buf1_avail += r; 
  223.         } 
  224.  
  225.         /* NB: read data from fd2 */ 
  226.         if (fd2 > 0) 
  227.         if (FD_ISSET (fd2, &rd)) { 
  228.             r = read (fd2, buf2 + buf2_avail, BUF_SIZE - buf2_avail); 
  229.             if (r < 1) { 
  230.                 SHUT_FD2; 
  231.             } else 
  232.                 buf2_avail += r; 
  233.         } 
  234.  
  235.         /* NB: write data to fd1 */ 
  236.         if (fd1 > 0) 
  237.         if (FD_ISSET (fd1, &wr)) { 
  238.             r = write (fd1, buf2 + buf2_written, buf2_avail - buf2_written); 
  239.             if (r < 1) { 
  240.                 SHUT_FD1; 
  241.             } else 
  242.                 buf2_written += r; 
  243.         } 
  244.  
  245.         /* NB: write data to fd1 */ 
  246.         if (fd2 > 0) 
  247.         if (FD_ISSET (fd2, &wr)) { 
  248.             r = write (fd2, buf1 + buf1_written, buf1_avail - buf1_written); 
  249.             if (r < 1) { 
  250.                 SHUT_FD2; 
  251.             } else 
  252.                 buf1_written += r; 
  253.         } 
  254.  
  255.         /* check if write data has caught read data */ 
  256.         if (buf1_written == buf1_avail) buf1_written = buf1_avail = 0; 
  257.         if (buf2_written == buf2_avail) buf2_written = buf2_avail = 0; 
  258.  
  259.         /* one side has closed connection, keep writing to the other side until empty */ 
  260.         if (fd1 < 0 && buf1_avail - buf1_written == 0) { 
  261.             SHUT_FD2; 
  262.         } 
  263.         if (fd2 < 0 && buf2_avail - buf2_written == 0) { 
  264.             SHUT_FD1; 
  265.         } 
  266.     } 
  267.     return 0; 
  268. /*----------------------源代碼結束--------------------------------------------*/ 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章