unix網路程式設計(1) 客戶端 伺服器第一版

2022-03-16 01:57:53 字數 3464 閱讀 1541

個人認為《unix網路程式設計》前4章可以好好看幾遍,不用先著急程式設計。另外作者提供的原始碼封裝太重,不如自己基於原始庫函式編寫客戶端以及伺服器,目前一些開源的專案也都是基於這些基礎庫函式的。

在了解了前四章的主要知識點後,比如socket、bind、connect、listen、accept等函式後,對網路程式設計有了一定的了解後,就可以參考第5章來寫自己的客戶端和伺服器了。對於新手來說這裡比較抽象,而且很多地方繞來繞去容易繞暈,需要重複看多次,再看後邊的章節。

這篇文章我就從第5章開始,仿照書上的demo寫乙個可以直接在單機上執行的cli-ser程式。

以下是server的對應程式:server.c

1 #include 2 #include 3 #include 4

5#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 }

編譯:gcc server.c -o server

這裡先列下經常用到的網路字段型別:

**流程:

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>

3 #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 }

編譯:gcc client.c -o client

客戶端的流程:

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...