Linux Socket基礎介紹

來源:互聯網
上載者:User

標籤:

Linux Socket函數庫是從Berkeley大學開發的BSD UNIX系統中移植過來的。BSD Socket介面是眾多Unix系統中被廣泛支援的TCP/IP通訊介面,Linux下的Socket程式設計,除了微小的差別之外,也適用於大多數其它Unix系統。

Socket介面是TCP/IP網路的API,Socket介面定義了許多函數或常式,程式員可以用它們來開發TCP/IP網路上的應用程式。網路的Socket資料轉送是一種特殊的I/O,Socket也是一種檔案描述符

Socket的使用和檔案操作比較類似。如同檔案的讀、寫、開啟、關閉等操作一樣,TCP/IP網路通訊同樣也有這些操作,不過它使用的介面不是檔案描述符或者FILE*,而是一個稱做Socket的描述符。類似於檔案操作,對於Socket,也通過讀、寫、開啟、關閉操作來進行網路資料傳送。同時,還有一些輔助的函數,如網域名稱/IP地址查詢、Socket功能設定等。

通訊端有三種類型:流式通訊端、資料通訊端及原始通訊端。

流式通訊端定義了一種可靠的連線導向的服務,實現了無差錯無重複的順序資料轉送。資料通訊端定義了一種不需連線的服務,資料通過相互獨立的報文進行傳輸,是無序的,並且不保證可靠、無差錯。原始通訊端允許對底層協議如IP或ICMP直接存取,主要用於新的網路通訊協定實現的測試等。

通訊端工作過程如下伺服器首先啟動,通過調用socket()建立一個通訊端,然後調用bind()將該通訊端和本網地址聯絡在一起,再調用listen()使通訊端做好偵聽的準備,並規定它的請求隊列的長度,之後就調用accept()來接收串連。客戶在建立通訊端後就可以調用connect()和伺服器建立串連。串連一旦建立,客戶機和伺服器之間就可以通過調用read()和write()來發送和接收資料。最後,待資料傳送結束後,雙方調用close()關閉通訊端。

Socket函數庫

1.        socket(int domain, int type,int protocol):此函數分配一個Socket控制代碼,用於指定特定網路下,使用特定的協議和資料傳送方式進行通訊。Socket控制代碼分配以後,如果要開始TCP通訊,還需要建立串連。根據需要,可以主動地建立串連(通過connect())和被動地等待對方建立串連(通過listen()),在串連建立後才能使用讀寫操作通過網路連接進行資料交換。

domain參數選擇通訊中使用的協議族,也就是網路的類型,可以是以下之一:(1)、AF_UNIX:UNIX內部協議;(2)、AF_INET:ARPA Internet協議,也就是TCP/IP協議族;(3)、AF_ISO:ISO協議;(4)、AF_NS:Xerox Network Systems協議;(5)、AF_IMPLINK:IMP “host at IMP” link layer。

type參數為資料傳送的方式,可以是以下之一:(1)、SOCK_STREAM:保證順序的、可靠傳送的雙向位元組資料流,最為常用,也是TCP串連所使用的方式;(2)、SOCK_DGRAM:不需連線的、不保證可靠的、固定長度(通常很小)的訊息傳送;(3)、SOCK_SEQPACKET:順序的、可靠的雙向固定長度的資料報傳送,只用於AF_NS類型的網路中;(4)、SOCK_RAW:原始的資料傳送,適用於系統內部專用的網路通訊協定和介面,和SOCK_RDM一樣,只能由超級使用者使用;(5)、SOCK_RDM:可靠的資料報傳送,未實現。

protocol參數指定通訊中使用的協議。在給定Socket的協議族和傳送類型之後,一般情況下所使用的協議也就固定下來,此時protocol參數可使用預設值”0”。但如果還有多個協議供選擇,則必須使用protocol參數來標識。

         socket()函數,正常執行時,返回Socket描述符;否則,返回-1,錯誤狀態在全域變數errno中。

2.        close(int fd):Socket和檔案描述符的關閉操作都是使用這個函數。fd參數為Socket描述符。傳回值,正常是返回0,-1表示出錯。

3.        bind(int sockfd, structsockaddr *my_addr, int addrlen):此函數給已經開啟的Socket指定本地地址。這個函數的使用有以下兩種情況:(1)、如果此Socket是連線導向的,而且此Socket在串連建立過程中處於被動的地位,即,乙方程式使用listen函數等待對方建立串連,對方用connect函數來向此Socket建立串連,這種情況下,必須用bind給此Socket設定本地地址。在乙方使用listen函數時,除指定Socket描述符之外,該Socket必須已經用bind函數設定好了本地地址(包括IP地址和連接埠號碼),這樣,系統在收到建立串連的網路請求時,才能根據請求的目的地址,識別是通向哪個Socket的串連,從而乙方才能用此Socket接收到發給此Socket地址的資料包。不指定Socket的本地地址,就無法將此Socket用於串連建立和資料接收。(2)、如果此Socket用於不需連線的情形,同樣也要求給該Socket設定本地地址,這樣,以後系統從網路中接收到資料後,才知道該送給哪個Socket及其相對應的進程。

sockfd參數:用於指定Socket描述符。

addrlen參數:my_addr結構的長度。

my_addr參數:用於偵聽串連請求的本地地址。struct sockaddr是一個通用性的結構,不僅包含TCP/IP協議的情況,同時也是為了適合於其它網路,如AF_NS。由於它的這種通用性,它只是定義了一個一般意義上的儲存空間。

bind函數傳回值:正常時返回0,否則返回-1,同時errno是系統錯誤碼。

4.        listen(int s, int backlog):準備接收串連請求。在用bind()給一個Socket設定本地地址後,就可以將這個Socket用於接收串連請求,即listen()。調用listen()之後,系統將給此Socket配備一個串連請求的隊列,暫存系統接收到的、申請向此Socket建立串連的請求,等待使用者程式用accept()正式接收該請求。隊列長度,就由backlog參數指定。如果短時間內向乙方建立串連的請求過多,乙方來不及處理,那麼排在backlog之後的請求將被系統拒絕。因此,backlog參數實際上規定了乙方程式能夠容許的串連建立處理速度。至於乙方程式使用此Socket(及其指定的本地地址)實際建立串連的個數,由乙方程式調用accept()的次數來決定。

listen函數傳回值:正常時返回0,否則返回-1.

5.        accept(int s, struct sockaddr*addr, int *addrlen):接受指定Socket上的串連請求。在調用listen()之後,系統就在Socket的串連請求暫存隊列裡存放每一個向該Socket(及其本地地址)建立的串連請求。accept()函數的作用就是,從該暫存隊列中取出一個串連請求,用該Socket的資料,建立一個新的Socket:Socket_New,並為它分配一個檔案描述符。Socket_New即標識了此次建立的串連,可被乙方用來向串連的另一方發生和接收資料(write/read, send/recv)。同時,原Socket仍然保持開啟狀態不變,繼續用於等待網路連接請求。

如果該Socket的暫存隊列中沒有待處理的串連請求,根據Socket的特徵選項(是否non_blocking),blocking即阻塞,accept()函數將選擇兩種方式:如果該Socket不是non_blocking型的,accept()將一直等待,直到收到一個串連請求後才返回;如果該Socket是non_blocking型的,那麼accept()將立即返回,但如果沒有串連請求,只返回錯誤資訊,不建立新的Socket_New。accept()返回後,如果建立了新的Socket_New來標識建立立的串連,那麼參數addr指定的結構裡面將會有對方的地址資訊,addrlen是地址資訊的長度。

s參數:Socket描述符。

addr參數:accept()接受串連後,在addr指向的結構中存放對方的地址資訊。如果是AF_INET Socket,該地址資訊就是地方的IP地址和連接埠號碼。

addrlen參數:在調用accept()之前,*addrlen必須被設定為addr資料結構的合法長度。在accept()返回之後,*addrlen中是對方地址資訊的長度。

accept函數傳回值:如果正常建立了一個新的串連,那麼返回非負的整數,即新串連的Socket描述符(注意,用於等待串連請求的原Socket保持開啟狀態不變,可用於接收新的串連請求),否則,返回-1.

bind、listen、connect等函數,都是用於被動地等待對方建立串連時需要使用的,而connect函數,則是主動地向對方建立串連時使用的。connect()使用一個事先開啟的Socket,和目的方(即通訊對方,或稱伺服器一方)地址資訊,向對方發出串連建立請求。

6.        connect(int sockfd, structsockaddr *serv_addr, int addrlen):用戶端發送服務要求。

sockfd參數:socket函數返回的socket描述符。

serv_addr:儲存遠端電腦的IP地址和連接埠資訊的結構。

addrlen:是結構體sockaddr_in的長度。

傳回值:成功返回0,否則返回-1.

7.        send(int s, const void *msg,int len, unsigned int flags)/sendto(int s, const void *msg, int len, unsignedint flags, const struct sockaddr *to, int tolen),recv(int s, void*buf, int len, unsigned iint flags)/recvfrom(int s, void *buf, int len,unsigned int flags, struct sockaddr *from, int *fromlen):用Socket發送和接收資料。在串連建立完成後,通訊雙方就可以使用以上這些函數來進行資料的發送和接收操作。其中,send和recv用於串連建立以後的發送和接收。sendto和recvfrom用於非串連的協議。對於非non_blocking型的Socket,send將等待資料發送完後才返回;對於non_blocking型的Socket,send將立即返回,使用者程式需要用select()函數決定網路發送是否結束。類似地,對於非non_blocking型的Socket,若系統沒有收到任何資料,recv將等待接收資料到達後才返回;對於non_blocking型的Socket,recv將立即返回,並返回錯誤資訊或接收到的資料位元組數。sendto和recvfrom因為是非串連型的發送和接收,必須在參數中給出目的地址或者存放源地址的空間。

s參數:Socket描述符。

msg,buf參數:存放接收或者發送資料的儲存空間。

len參數:接收或者發送資料的位元組數。

to,from參數:sendto和recvfrom所使用的,目的方地址和存放源地址的空間。

tolen,fromlen參數:目的地址和源地址空間大小。

flag參數:通常設為0.

傳回值:send/sendto返回實際發送的資料位元組數,或者-1,表示出錯。recv/recvfrom返回實際接收到的資料位元組數,或者-1,表示出錯。

8.        read(int fd, void *buf, size_tcount)/write(int fd, const void *buf, size_t count):用系統檔案操作進行Socket通訊。在串連建立完成後,對於串連建立過程中被動的一方,在accept()正常返回後,它返回一個新的Socket,並且為該Socket分配了一個檔案描述符;對於串連請求發起方,connect()正常返回後,相應的Socket中也包含有已指派的檔案描述符。因此,可以使用標準的Unix檔案讀寫函數read()/write()來進行Socket通訊。要注意的是,由於網路資料和磁碟檔案不一樣,不是已經準備好的,因此,每次讀寫操作不一定能傳送完指定長度的資料,需要由程式反覆進行剩餘部分的傳送。另外,檔案描述符是較底層的檔案操作函數,不同於C語言中常用的FILE*。FILE*是使用fread/fwrite函數來進行讀寫操作的。

fd參數:檔案或者Socket描述符。

buf參數:資料緩衝區。

count參數:資料位元組數。

函數傳回值:正常時,返回所讀寫的位元組數(注意,可能小於count參數指定的數目);否則,返回-1.

9.        getsockopt(int s, int level,int optname, void *optval, int *optlen)/setsockopt(int s, int level, intoptname, const void *optval, int optlen):擷取、設定Socket特徵選項。

常用的幾個轉換函式:(1)、inet_addr:將IP地址從點數格式轉換成無符號長整型,它返回的地址是網路位元組格式;(2)、inet_ntoa:將一個in_addr結構體輸出成點數格式;(3)、htonl:將32位的主機位元組順序轉化為32位的網路位元組順序;(4)、htons:將16位的主機位元組順序轉化為16位的網路位元組順序;(5)、ntohs:將一個無符號短整形數從網路位元組順序轉化為主機位元組順序;(6)、ntohl:將一個無符號長整形數從網路主機順序轉化為主機位元組順序。

以下是測試案例:

1. client.cpp:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <arpa/inet.h>#include <iostream>#define PORT 7000int main(){struct sockaddr_in server;int s, ns;int pktlen, buflen;char buf1[256], buf2[256];s = socket(AF_INET, SOCK_STREAM, 0);server.sin_family = AF_INET;server.sin_port = htons(PORT);server.sin_addr.s_addr = htons(INADDR_ANY);if (connect(s, (struct sockaddr*)&server, sizeof(server)) < 0) {perror("connect()");return -1;}for (;;) {printf("Enter a line: ");std::cin>>buf1;buflen = strlen(buf1);if (buflen == 1)break;send(s, buf1, buflen+1, 0);recv(s, buf2, sizeof(buf2), 0);printf("Received line: %s\n", buf2);}close(s);return 0;}
2. server.cpp:

#include <stdio.h>#include <string.h>#include <netinet/in.h>#include <sys/types.h>#include <sys/socket.h>#include <stdlib.h>#include <unistd.h>#include <ctype.h>#define PORT 7000int main(){struct sockaddr_in client, server;int s, ns, pktlen;char buf[256];s = socket(AF_INET, SOCK_STREAM, 0);memset((char*)&server, sizeof(server), 0);server.sin_family = AF_INET;server.sin_port = htons(PORT);server.sin_addr.s_addr = htons(INADDR_ANY);bind(s, (struct sockaddr*)&server, sizeof(server));listen(s, 1);socklen_t namelen = sizeof(client);ns = accept(s, (struct sockaddr*)&client, &namelen);for (;;) {pktlen = recv(ns, buf, sizeof(buf), 0);if (pktlen == 0)break;printf("Received line:%s\n", buf);for (int i = 0; i < strlen(buf); i++)buf[i] = toupper(buf[i]);send(ns, buf, pktlen, 0);}close(ns);close(s);return 0;}

執行說明:(1)、開啟終端,分別執行:$ g++ -o server server.cpp  , $ g++ -o client client.cpp ; (2)、開啟終端,先執行伺服器端程式:$ ./server ;再開啟另一終端,執行用戶端程式:$ ./client  ;(3)、程式功能:伺服器端接收從用戶端來的資料,並將其接收的資料小寫字母改為大寫再發送給用戶端,在用戶端顯示接收後的結果資料。當輸入一個字元長度時退出。

以上部分內容整理自網路。



Linux Socket基礎介紹

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.