個人認為《unix網路程式設計》前4章可以好好看幾遍,不用先著急程式設計。另外作者提供的原始碼封裝太重,不如自己基於原始庫函式編寫客戶端以及伺服器,目前一些開源的專案也都是基於這些基礎庫函式的。
在了解了前四章的主要知識點後,比如socket、bind、connect、listen、accept等函式後,對網路程式設計有了一定的了解後,就可以參考第5章來寫自己的客戶端和伺服器了。對於新手來說這裡比較抽象,而且很多地方繞來繞去容易繞暈,需要重複看多次,再看後邊的章節。
這篇文章我就從第5章開始,仿照書上的demo寫乙個可以直接在單機上執行的cli-ser程式。
以下是server的對應程式:server.c
1 #include 2 #include 3 #include 4編譯:gcc server.c -o server5#define maxline 102467
extern
interrno;89
void str_echo(int
);10
11int
main() 34}
3536 printf("
server end!\n");
37return0;
38}3940
void str_echo(int
sockfd) else
if (n < 0
) 56
}57 }
這裡先列下經常用到的網路字段型別:
**流程:
1、申請socket
伺服器首先申請socket,socket類似於再unix系統上開啟乙個檔案,會返回乙個檔案標識號用來標識當前開啟的檔案。
socket需要引用標頭檔案
int socket(int family, int type, int protocol);
family:對應的是協議族,ipv4:af_inet ipv6:af_inet6
type:套接字型別,tcp對應socket_stream(資料流)
protocol:協議型別,這裡我們用0,核心會根據family和type選擇預設的協議,對於family:af_inet,type:sock_stream,預設的協議是tcp
2、埠繫結
一般伺服器啟動乙個服務程序會開啟某個埠的監聽工作,所以一般的伺服器程序需要繫結固定的埠號,也就是該程序對應的socket需要繫結到某乙個埠號。對於多網絡卡的伺服器,會對應多個ip,當然也可以繫結固定的ip,我們這裡不進行繫結 ,使用通配位址(ipv4:inaddr_any,ipv6:in6addr_any_init),此處的埠或ip繫結用的函式是bind
#include
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
sockfd:監聽套接字,對於伺服器來說,即調socket返回的套接字
myaddr:套接字結構體,我們一般會先申請乙個sockaddr_in結構的套接字,通過bzero函式(string.h的乙個函式)進行結構體初始化為0,分別對family,ip,port填值,然後用sockaddr強制型別轉化進行呼叫,具體的可以參考書中bind函式使用;
addrlen:為套接字結構體長度
3、套接字埠監聽
目前已經在申請好的套接字上進行了監聽ip及port的初始化,那麼可以核心開始按照我們初始化的資訊進行監聽了,即呼叫listen函式,核心會申請乙個佇列用於存放未完成連線以及已完成連線的套接字,如下圖
對映到tcp的三次握手,如下圖:
4、與客戶端建立連線
下邊我們會進入乙個無限迴圈,會一直處理client發來的tcp鏈結,accept為阻塞函式,如果沒有客戶端連線,這個函式會被阻塞,也就是程式會在這裡停止,知道有client建立了tcp連線accept才返回,accept返回也就說明,此時已經建立好一條tcp連線通路,下邊我們的伺服器會在這條通路上進行資料的傳送與接收,至於接收後會怎麼處理,以及返回客戶端什麼資料,就屬於伺服器自己的業務需求了。我們這裡會fork乙個子程序進行這些邏輯的處理。為什麼要建立子程序呢?我們的伺服器程序是併發的伺服器,如果accept後,程序開始處理業務邏輯,那麼其他的client需要等待這條tcp完成邏輯處理後,才能進入下一次迴圈。所以我們新建子程序專門用於邏輯的處理,至於父程序就專門負責accept,建立新的鏈結,這樣多個client發起與伺服器的tcp鏈結,伺服器主程序可以一直迴圈accept建立連線,然後fork子程序進行後續處理,這樣我們就實現了簡單的併發伺服器,可以同時與多個client建立tcp連線。
#include
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
這裡有一點需要注意,addrlen使用的是指標,這是由於addrlen的入參會被核心使用到,已提醒讀取cliaddr的長度,另外,核心會寫回cliaddr,這也防止記憶體溢位,並且寫入多少這個數,核心還會寫回addrlen,這裡乙個引數做了多個事情,所以用了值—引數這種指標傳參。
#include
pid_t fork(void);
建立子程序,對於父程序返回值為子程序的程序id,對於子程序返回0。
以下是client**:client.c
1 #include 2 #include in.h>編譯:gcc client.c -o client3 #include 4 #include
5 #include 6 #include 7 #include 8
9#define maxline 1024
1011
void str_cli(file *, int
);12
13int
main()
3132
void str_cli(file *fp, int
sockfd)
41fputs(recvline, stdout);
42bzero(recvline, maxline);43}
44 }
客戶端的流程:
1、建立套接字
2、發起tcp連線
#include
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
connect也是阻塞函式,tcp連線成功後返回0。
到這裡我們完成了乙個超級簡單的伺服器-客戶端程式的開發。之後我們會對這個程式不斷完善。
下文:
網路程式設計基礎(伺服器,客戶端)
伺服器端程式設計步驟 1 建立套接字socket,初始化網路結構struct sockaddr in 2 bind繫結 伺服器必須執行此函式,客戶端可選 3 listen 設定監聽數 4 accept接受連線請求 客戶端程式設計步驟 1 建立套接字socket,初始化網路結構 2 connect向伺...
Linux 網路程式設計 基本伺服器 客戶端
前言 我們開始學習網路程式設計,儘管接下來的速度會非常快,但是我還是希望事事具細。首先我們以乙個回射伺服器來說說流程。變數部分 listenfd 監聽字 connfd 鏈結字 childpid 伺服器程序id cliaddr 客戶端套接字結構體 servaddr 服務端套接字結構體 函式部分 soc...
網路程式設計(伺服器與客戶端交流)
網路程式設計 tcp協議 c s程式 伺服器與客戶端 b s是網頁與客戶端 需要使用兩個類,來編寫tcp協議的cs程式 1.serversocket 搭建伺服器 2.socket 搭建客戶端 兩方使用socket 套接字,通訊端點 進行交接 serversocket 構造方法 serversocke...