socket程式設計 TCP

2022-06-21 23:03:12 字數 4164 閱讀 2526

socket本身有「插座」的意思,在linux環境下,用於表示程序間網路通訊的特殊檔案型別。本質為核心借助緩衝區形成的偽檔案

既然是檔案,那麼理所當然的,我們可以使用檔案描述符引用套接字。與管道類似的,linux系統將其封裝成檔案的目的是為了統一介面,使得讀寫套接字和讀寫檔案的操作一致。區別是管道主要應用於本地程序間通訊,而套接字多應用於網路程序間資料的傳遞

套接字通訊原理如下圖所示:

在網路通訊中,套接字一定是成對出現的。一端的傳送緩衝區對應對端的接收緩衝區。我們使用同乙個檔案描述符索傳送緩衝區和接收緩衝區。

我們已經知道,記憶體中的多位元組資料相對於記憶體位址有大端和小端之分,磁碟檔案中的多位元組資料相對於檔案中的偏移位址也有大端小端之分。網路資料流同樣有大端小端之分,那麼如何定義網路資料流的位址呢?傳送主機通常將傳送緩衝區中的資料按記憶體位址從低到高的順序發出,接收主機把從網路上接到的位元組依次儲存在接收緩衝區中,也是按記憶體位址從低到高的順序儲存,因此,網路資料流的位址應這樣規定:先發出的資料是低位址,後發出的資料是高位址。

tcp/ip協議規定,網路資料流應採用大端位元組序,即低位址高位元組。例如上一節的udp段格式,位址0-1是16位的源埠號,如果這個埠號是1000(0x3e8),則位址0是0x03,位址1是0xe8,也就是先發0x03,再發0xe8,這16位在傳送主機的緩衝區中也應該是低位址存0x03,高位址存0xe8。但是,如果傳送主機是小端位元組序的,這16位被解釋成0xe803,而不是1000。因此,傳送主機把1000填到傳送緩衝區之前需要做位元組序的轉換。同樣地,接收主機如果是小端位元組序的,接到16位的源埠號也要做位元組序的轉換。如果主機是大端位元組序的,傳送和接收都不需要做轉換。同理,32位的ip位址也要考慮網路位元組序和主機位元組序的問題。

為使網路程式具有可移植性,使同樣的c**在大端和小端計算機上編譯後都能正常執行,可以呼叫以下庫函式做網路位元組序和主機位元組序的轉換

#include uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

h表示host,n表示network,l表示32位長整數,s表示16位短整數。

如果主機是小端位元組序,這些函式將引數做相應的大小端轉換然後返回,如果主機是大端位元組序,這些函式不做轉換,將引數原封不動地返回。

int inet_aton(const

char *cp, struct in_addr *inp);

in_addr_t inet_addr(

const

char *cp);

char *inet_ntoa(struct in_addr in);

只能處理ipv4的ip位址,

不可重入函式

int inet_pton(int af, const

char *src, void *dst);

const

char *inet_ntop(int af, const

void *src, char *dst, socklen_t size);

支援ipv4和ipv6,可重入函式,其中inet_pton和inet_ntop不僅可以轉換ipv4的in_addr,還可以轉換ipv6的in6_addr。

strcut sockaddr 很多網路程式設計函式誕生早於ipv4協議,那時候都使用的是sockaddr結構體,為了向前相容,現在sockaddr退化成了(void *)的作用,傳遞乙個位址給函式,至於這個函式是sockaddr_in還是sockaddr_in6,由位址族確定,然後函式內部再強制型別轉化為所需的位址型別。

int socket(int domain, int type, int protocol);
引數:domain:

type:

protocol:

返回值:

成功:返回指向新建立的socket的檔案描述符,失敗:返回-1,設定errno

功能:

int bind(int sockfd, const

struct sockaddr *addr, socklen_t addrlen);

引數:sockfd:

addr:

addrlen:

返回值:

成功返回0,失敗返回-1, 設定errno

功能:。

int listen(int sockfd, int backlog);
引數:sockfd:

socket檔案描述符

backlog:

排隊建立3次握手佇列和剛剛建立3次握手佇列的鏈結數和

返回值:

成功返回0,失敗返回-1

功能:典型的伺服器程式可以同時服務於多個客戶端,當有客戶端發起連線時,伺服器呼叫的accept()返回並接受這個連線,如果有大量的客戶端發起連線而伺服器來不及處理,尚未accept的客戶端就處於連線等待狀態,listen()宣告sockfd處於監聽狀態,並且最多允許有backlog個客戶端處於連接待狀態,如果接收到更多的連線請求就忽略。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
引數:sockdf:

socket檔案描述符

addr:

addrlen:

傳入傳出引數(值-結果),傳入sizeof(addr)大小,函式返回時返回真正接收到位址結構體的大小

返回值:

成功返回乙個新的socket檔案描述符,用於和客戶端通訊,失敗返回-1,設定errno

功能:三方握手完成後,伺服器呼叫accept()接受連線,如果伺服器呼叫accept()時還沒有客戶端的連線請求,就阻塞等待直到有客戶端連線上來。addr是乙個傳出引數,accept()返回時傳出客戶端的位址和埠號。addrlen引數是乙個傳入傳出引數(value-result argument),傳入的是呼叫者提供的緩衝區addr的長度以避免緩衝區溢位問題,傳出的是客戶端位址結構體的實際長度(有可能沒有佔滿呼叫者提供的緩衝區)。如果給addr引數傳null,表示不關心客戶端的位址。

int connect(int sockfd, const

struct sockaddr *addr, socklen_t addrlen);

引數:sockdf:

socket檔案描述符

addr:

傳入引數,指定伺服器端位址資訊,含ip位址和埠號

addrlen:

傳入引數,傳入sizeof(addr)大小

返回值:

成功返回0,失敗返回-1,設定errno

功能:客戶端需要呼叫connect()連線伺服器,connect和bind的引數形式一致,區別在於bind的引數是自己的位址,而connect的引數是對方的位址。connect()成功返回0,出錯返回-1。

Socket 程式設計(TCP)

詳細請參見 點我o o哈!很不錯的一篇文章哈 下面是接收資料和傳送資料的函式 int receive socket fd,char sztext,int len if rc 0 return len cnt sztext rc cnt rc return len int send socket fd...

Socket 程式設計(TCP)

下面是接收資料和傳送資料的函式 cpp view plain copy intreceive socket fd,char sztext,intlen if rc 0 return len cnt sztext rc cnt rc return len int send socket fd,char...

socket程式設計TCP

注意一定要先啟動server,再啟動client,否則client因為無法找到server而丟擲異常 coding utf 8 from socket import serverport 12000 af inet表示底層網路使用的是ipv4,sock stream表示使用的socket型別是tcp...