一個簡單Linux服務器程序的實現

1.前言

        本文將介紹一個簡單 Linux 服務器程序的實現,主要功能是隻要客戶端發送連接請求過來,就發送"Hello World"給客戶端。

 

2.一些簡單概念

套接字

        在 TCP/IP 協議中,"IP+端口" 可以唯一標識網絡通訊中的一個進程。套接字(socket)本身有插座的意思,它是用於進程間網絡通訊的一種特殊文件類型,這種文件跟普通文件不一樣,普通文件需要存儲在硬盤上,而 socket 文件是藉助內核緩衝區形成的僞文件。在 Linux 讀寫普通文件時,我們先使用 open 函數打開文件然後獲取文件句柄 fd,然後調用 read 函數就可以讀取文件內容,調用 write 函數就可以往文件寫入內容。socket 也可以類似理解爲這樣的文件描述符,可以理解爲:如果網絡通訊中兩個進程要通訊就需要藉助套接字(socket)來實現。

 

        假設現在服務器創建了一個 socket 並且處於監聽狀態,客戶端也創建了一個 socket 與服務器建立起連接,然後客戶端就可以通過 write 給服務器發送數據,通過 read 讀取服務器發送來的數據。

 

CS模型

        C/S 模型又稱爲客戶端/服務器模型,對於 TCP 客戶端/服務器模型開發一般如下圖所示。

 

3.簡單Linux服務器程序實現

  1 #include <sys/types.h>
  2 #include <sys/socket.h>
  3 #include <string.h>
  4 #include <netinet/in.h>
  5 #include <arpa/inet.h>
  6 #include <unistd.h>
  7 #include <stdio.h>
  8 
  9 #define SERVER_PORT 8080
 10 
 11 int main()
 12 {
 13     int listen_fd = socket(AF_INET,SOCK_STREAM,0);
 14     int conn_fd = 0;
 15     char data[] = "Hello World!";
 16 
 17     struct sockaddr_in server_addr;
 18     memset(&server_addr,0,sizeof(server_addr));
 19     server_addr.sin_family = AF_INET;
 20     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 21     server_addr.sin_port = htons(SERVER_PORT);
 22     
 23     bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
 24     listen(listen_fd,32);
 25     
 26     while(1)
 27     {
 28         conn_fd = accept(listen_fd,(struct sockaddr *)NULL,NULL);
 29         write(conn_fd,data,strlen(data));
 30         close(conn_fd);
 31     }
 32     close(listen_fd);
 33
 34     return 0;
 35 }

程序講解

1. 首先調用一個 socket 函數創建一個監聽套接字,第一個參數 AF_INET 代表是 IPv4 地址,第二個參數 SOCK_STREAM 代表流式協議,第三個參數 0 代表默認協議,這裏是 TCP。

2. 將套接字與具體的 IP 和端口綁定,需要調用 bind 函數,bind 函數的第一個參數是需要綁定的 socket,第二個參數是一個 struct sockaddr 類型的指針,第三個參數是 struct sockaddr 類型的指針指向的數據長度。可以看到,創建了 struct sockaddr_in 結構體後,將其程序 sin_family 指定爲 AF_INET,即 IPv4 類型;將 sin_addr.s_addr 指定爲 INADDR_ANY 代表使用主機的任意一個地址(因爲一個電腦可以有多個網卡,擁有多個 IP 地址);將 sin_port 指定爲 8080,這樣該套接字就與一個本地 IP 和 8080 端口綁定起來了。

3.調用 listen 函數指定處於連接就緒隊列的連接數最大值。

4.在 while(1) 中 accept 阻塞等待客戶端發送連接請求過來,一旦成功與客戶端連接,accept 函數就返回一個與客戶端通訊的通訊套接字(注意監聽套接字與通訊套接字不同),然後往通訊套接字寫入數據,再調用 close 關閉連接。

關於上述一些 API 的具體含義將會在後續文章做深入講解,這裏只需要先簡單瞭解含義即可。

 

4.功能調試

編寫完 server.c 文件後,需要編譯產生可執行文件然後運行。

編譯:gcc server.c -o server

運行:./server

運行 server 程序後,需要有對應的客戶端程序去發起訪問,這裏介紹一個好用的客戶端工具:telnet。

Ubuntu 下安裝 telnet:sudo apt-get install telnet

telnet 使用,在命令行模式下輸入:telnet IP地址 端口,如 telnet 127.0.0.1 8080 就可以發起對本地 8080 端口的訪問了。

如上,使用 telnet 往 127.0.0.1 的 8080 端口建立連接後,客戶端收到了 Hello,world!,這句話是服務器程序 write 函數調用的結果,然後顯示 Connection closed by foreign ost,這是因爲每次連接後服務器發送完 Hello,world! 就 close 掉這個 socket 了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章