1. 程式人生 > >C 網路程式設計 socket

C 網路程式設計 socket

 Socket描述符是個指向內部資料結構的指標,他指向描述符表入口。呼叫Socket函式時,socket執行體將建立一個Socket,實際上"建立一個Socket"意味著為一個Socket資料結構分配儲存空間。Socket執行體為您管理描述符表。  兩個網路程式之間的一個網路連線包括五種資訊:通訊協議、本地協議地址、本地主機埠、遠端主機地址和遠端協議埠。Socket資料結構中包含這五種資訊。
Socket
配置通過socket呼叫返回一個socket描述符後,在使用socket進行網路傳輸以前,必須配置該socket。面向連線的socket客戶端通過呼叫Connect函式在socket資料結構中儲存本地和遠端資訊

。無連線socket的客戶端和服務端連同面向連線socket的服務端通過呼叫 bind函式來配置本地資訊
Bind
函式將socket和本機上的一個埠相關聯,隨後您就能夠在該埠監聽服務請求。Bind函式原型為: int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
 Sockfd是呼叫socket函式返回的socket描述符,my_addr是個指向包含有本機IP地址及埠號等資訊的sockaddr型別的指標;addrlen常被配置為sizeof(struct sockaddr) struct sockaddr結構型別是用來儲存socket
資訊的: struct sockaddr {
 unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 
位元組的協議地址 */
};
 sa_family一般為AF_INET,代表InternetTCP/IP)地址族;sa_data則包含該socketIP地址和埠號。另外更有一種結構型別: struct sockaddr_in {
 short int sin_family; /* 地址族 */
 unsigned short int sin_port; /* 埠號 */
 struct in_addr sin_addr; /* IP地址 */
 unsigned char sin_zero[8]; /* 填充以保持和struct sockaddr同樣大小 */
 };
這個結構更方便使用。sin_zero用來將sockaddr_in結構填充到和struct sockaddr同樣的長度,能夠用bzero()memset()函式將其置為零。指向sockaddr_in 的指標和指向sockaddr的指標能夠相互轉換,這意味著假如一個函式所需引數型別是sockaddr時,您能夠在函式呼叫的時候將一個指向 sockaddr_in的指標轉換為指向sockaddr的指標;或相反。  使用bind函式時,能夠用下面的賦值實現自動獲得本機IP地址和隨機獲取一個沒有被佔用的埠號: my_addr.sin_port = 0; /* 系統隨機選擇一個未被使用的埠號 */ my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本機IP地址 */通過將my_addr.sin_port置為0,函式會自動為您選擇一個未佔用的埠來使用。同樣,通過將my_addr.sin_addr.s_addr置為INADDR_ANY,系統會自動填入本機IP地址。注意在使用bind函式是需要將sin_portsin_addr轉換成為網路位元組優先順序;而sin_addr則無需轉換。  電腦資料儲存有兩種位元組優先順序:高位位元組優先和低位位元組優先。Internet上資料以高位位元組優先順序在網路上傳輸,所以對於在內部是以低位位元組優先方式儲存資料的機器,在Internet上傳輸資料時就需要進行轉換,否則就會出現資料不一致。下面是幾個位元組順序轉換函式:
·htonl()
:把32位值從主機位元組序轉換成網路位元組序
·htons()
:把16位值從主機位元組序轉換成網路位元組序
·ntohl()
:把32位值從網路位元組序轉換成主機位元組序
·ntohs()
:把16位值從網路位元組序轉換成主機位元組序 Bind()函式在成功被呼叫時返回0;出現錯誤時返回"-1"並將errno置為相應的錯誤號。需要注意的是,在呼叫bind函式時一般不要將埠號置為小於1024的值,因為11024是保留埠號,您能夠選擇大於1024中的任何一個沒有被佔用的埠號。連線建立  面向連線的客戶程式使用Connect函式來配置socket並和遠端伺服器建立一個TCP連線,其函式原型為: int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);
Sockfd 
socket函式返回的socket描述符;serv_addr是包含遠端主機IP地址和埠號的指標addrlen是遠端地質結構的長度。 Connect函式在出現錯誤時返回-1,並且配置errno為相應的錯誤碼。進行客戶端程式設計無須呼叫bind(),因為這種情況下只需知道目的機器的IP地址,而客戶通過哪個埠和伺服器建立連線並無需關心,socket執行體為您的程式自動選擇一個未被佔用的埠,並通知您的程式資料什麼時候到達埠。 Connect函式啟動和遠端主機的直接連線。只有面向連線的客戶程式使用socket時才需要將此socket和遠端主機相連。無連線協議從不建立直接連線。面向連線的伺服器也從不啟動一個連線,他只是被動的在協議埠監聽客戶的請求。 Listen函式使socket處於被動的監聽模式,併為該socket建立一個輸入資料佇列,將到達的服務請求儲存在此佇列中,直到程式處理他們。 int listen(int sockfd int backlog);
Sockfd 
Socket系統呼叫返回的socket 描述符;backlog指定在請求佇列中允許的最大請求數,進入的連線請求將在佇列中等待accept()他們(參考下文)。Backlog對佇列中等待服務的請求的數目進行了限制,大多數系統預設值為20。假如一個服務請求到來時,輸入佇列已滿,該socket將拒絕連線請求,客戶將收到一個出錯資訊。當出現錯誤時listen函式返回-1,並置相應的errno錯誤碼。 accept()函式讓伺服器接收客戶的連線請求。在建立好輸入佇列後,伺服器就呼叫accept函式,然後睡眠並等待客戶的連線請求。 int accept(int sockfd, void *addr, int *addrlen);
sockfd
是被監聽的socket描述符,addr通常是個指向sockaddr_in變數的指標,該變數用來存放提出連線請求服務的主機的資訊(某臺主機從某個埠發出該請求)addrten通常為一個指向值為sizeof(struct sockaddr_in)的整型指標變數。出現錯誤時accept函式返回-1並置相應的errno值。  首先,當accept函式監控的 socket收到連線請求時,socket執行體將建立一個新的socket,執行體將這個新socket和請求連線程序的地址聯絡起來,收到服務請求的初始socket仍能夠繼續在以前的 socket上監聽,同時能夠在新的socket描述符上進行資料傳輸操作。資料傳輸 Send()recv()這兩個函式用於面向連線的socket上進行資料傳輸。 Send()函式原型為: int send(int sockfd, const void *msg, int len, int flags);
Sockfd
是您想用來傳輸資料的socket描述符;msg是個指向要傳送資料的指標;Len是以位元組為單位的資料的長度;flags一般情況下置為0(關於該引數的用法可參照man手冊)。 Send()函式返回實際上傳送出的位元組數,可能會少於您希望傳送的資料。在程式中應該將send()的返回值和欲傳送的位元組數進行比較。當send()返回值和len不匹配時,應該對這種情況進行處理。
char *msg = "Hello!";
int len, bytes_sent;
……
len = strlen(msg);
bytes_sent = send(sockfd, msg,len,0);
……
 recv()函式原型為: int recv(int sockfd,void *buf,int len,unsigned int flags);
 Sockfd是接受資料的socket描述符;buf 是存放接收資料的緩衝區;len是緩衝的長度。Flags也被置為0Recv()返回實際上接收的位元組數,當出現錯誤時,返回-1並置相應的errno值。
Sendto()
recvfrom()用於在無連線的資料報socket方式下進行資料傳輸。由於本地socket並沒有和遠端機器建立連線,所以在傳送資料時應指明目的地址
sendto()
函式原型為: int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
  該函式比send()函式多了兩個引數,to表示目地機的IP地址和埠號資訊,而tolen常常被賦值為sizeof (struct sockaddr)Sendto 函式也返回實際傳送的資料位元組長度或在出現傳送錯誤時返回-1 Recvfrom()函式原型為: int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
from
是個struct sockaddr型別的變數,該變數儲存源機的IP地址及埠號。fromlen常置為sizeof (struct sockaddr)。當recvfrom()返回時,fromlen包含實際存入from中的資料位元組數。Recvfrom()函式返回接收到的位元組數或當出現錯誤時返回-1,並置相應的errno假如您對資料報socket呼叫了connect()函式時,您也能夠利用send()recv()進行資料傳輸,但該socket仍然是資料報socket,並且利用傳輸層的UDP服務。但在傳送或接收資料報時,核心會自動為之加上目地和源地址資訊。結束傳輸  當任何的資料操作結束以後,您能夠呼叫close()函式來釋放該socket,從而停止在該socket上的任何資料操作:
close(sockfd);
  您也能夠呼叫shutdown()函式來關閉該socket。該函式允許您只停止在某個方向上的資料傳輸,而一個方向上的資料傳輸繼續進行。如您能夠關閉某socket的寫操作而允許繼續在該socket上接受資料,直至讀入任何資料。 int shutdown(int sockfd,int how); Sockfd是需要關閉的socket的描述符。引數 how允許為shutdown操作選擇以下幾種方式: ·0-------不允許繼續接收資料 ·1-------不允許繼續傳送資料 ·2-------不允許繼續傳送和接收資料, ·均為允許則呼叫close ()
 shutdown在操作成功時返回0,在出現錯誤時返回-1並置相應errno