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 了。