前沿拓展:
本文主要給大家分享網絡七層概念之網絡編程socket,前邊的章節(jié)已經給大家講述了鏈路層、物理層、網絡層、應用層、傳輸層等,歡迎學習嵌入式網絡編程的朋友關注、轉載和發(fā)表評論!
(絕對的好文,建議先收藏和轉載?。?/p>
*/ bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)); listen(sockfd, BACKLOG);
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd, &their_addr, &sin_size);
……
……
}
注意:我們使用了套接字描述符 new_fd 用來進行所有的 send() 和 recv() 調用。如果你只想獲
得一個單獨的連接,那么你可以將原來的 sock_fd 關掉(調用 close()),這樣的話就可以阻止以后的連接了。
在面向連接的通信中客戶機要做如下一些事:
· 調用 socket() 函數創(chuàng)建一個套接字。
· 調用 connect() 函數試圖連接服務。
· 如果連接成功調用 write() 函數請求數據,調用 read() 函數接收引入的應答。
8.5.6 send()和 recv()函數
這兩個函數是最基本的,通過連接的套接字流進行通訊的函數。
如果你想使用無連接的用戶數據報的話,請參考下面的 sendto() 和 recvfrom() 函數。
send() 函數的聲明:
#include <sys/types.h>
#include <sys/socket.h>
int send(int sockfd, const void *msg, int len, int flags);
send 的參數含義如下:
· sockfd 是代表你與遠程程序連接的套接字描述符。
· msg 是一個指針,指向你想發(fā)送的信息的地址。
· len 是你想發(fā)送信息的長度。
· flags 發(fā)送標記。一般都設為 0(你可以查看 send 的 man pages 來獲得其他的參數值并且明白各個參數所代表的含義)。
下面看看有關 send() 函數的代碼片段: char *msg = ” Hello! World!”;
int len, bytes_sent;
……
……
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);
……
……
……
send()函數在調用后會返回它真正發(fā)送數據的長度。
注意:send() 所發(fā)送的數據可能少于你給它的參數所指定的長度!
因為如果你給 send() 的參數中包含的數據的長度遠遠大于 send() 所能一次發(fā)送的數據,則
send() 函數只發(fā)送它所能發(fā)送的最大數據長度,第二它相信你會把剩下的數據再次調用它來進行第二次發(fā)送。
所以,記住如果 send() 函數的返回值小于 len 的話,則你需要再次發(fā)送剩下的數據。幸運的是, 如果包足夠?。ㄐ∮?1K),那么 send() 一般都會一次發(fā)送光的。
像上面的函數一樣,send() 函數如果發(fā)生錯誤,則返回 – 1,錯誤代碼存儲在全局變量 errno中。
下面我們來看看 recv() 函數。
函數 recv() 調用在許多方面都和 send() 很相似,下面是 recv() 函數的聲明:
#include <sys/types.h>
#include <sys/socket.h>
int recv(int sockfd, void *buf, int len, unsigned int flags);
recv()的參數含義如下:
· sockfd 是你要讀取數據的套接字描述符。
· buf 是一個指針,指向你能存儲數據的內存緩存區(qū)域。
· len 是緩存區(qū)的最大尺寸。
· flags 是 recv() 函數的一個標志, 一般都為 0(具體的其他數值和含義請參考 recv() 的man pages)。
recv() 返回它所真正收到的數據的長度。(也就是存到 buf 中數據的長度)。如果返回 –1 則代表發(fā)生了錯誤(比如網絡以外中斷、對方關閉了套接字連接等),全局變量 errno 里面存儲了錯誤代碼。
很簡單,不是嗎?現在你已經可以使用套接字連接進行網絡發(fā)送數據和接受數據了!
Ya! 你現在已經成為了一個 Linux 下的網絡程序員了!
8.5.7 sendto()和 recvfrom()函數
這兩個函數是進行無連接的 UDP 通訊時使用的。使用這兩個函數,則數據會在沒有建立過任何連接的網絡上傳輸。因為數據報套接字無法對遠程主機進行連接,想想我們在發(fā)送數據前需要知道些什么 呢?
對了!是遠程主機的 IP 地址和端口!
下面是 sendto()函數和 recvfrom()函數的聲明:
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
和你所看到的一樣,這個函數和 send()函數基本一致。
· sockfd 是代表你與遠程程序連接的套接字描述符。
· msg 是一個指針,指向你想發(fā)送的信息的地址。
· len 是你想發(fā)送信息的長度。
· flags 發(fā)送標記。一般都設為 0。 (你可以查看 send 的 man pages 來獲得其他的參數值并且明白各個參數所代表的含義)
· to 是一個指向 struct sockaddr 結構的指針,里面包含了遠程主機的
IP 地址和端口數據。
· tolen 只是指出了 struct sockaddr 在內存中的大小sizeof(struct sockaddr)。
和 send() 一樣,sendto() 返回它所真正發(fā)送的字節(jié)數(當然也和 send() 一樣,它所真正發(fā)送的字節(jié)數可能小于你所給它的數據的字節(jié)數)。 當它發(fā)生錯誤的時候,也是返回– 1,同時全局變量
errno 存儲了錯誤代碼。
同樣的,recvfrom() 函數和 recv() 函數也基本一致。
recvfrom() 的聲明為:
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
其參數含義如下:
· sockfd 是你要讀取數據的套接字描述符。
· buf 是一個指針,指向你能存儲數據的內存緩存區(qū)域。
· len 是緩存區(qū)的最大尺寸。
· flags 是 recvfrom() 函數的一個標志, 一般都為 0 (具體的其他數值和含義請參考 recvfrom() 的 man pages)。
· from 是一個本地指針,指向一個 struct sockaddr 的結構(里面存有源 IP 地址和端口數)。
· fromlen 是一個指向一個 int 型數據的指針,它的大小應該是sizeof(struct sockaddr).當函數返回的時候,
formlen 指向的數據是 form 指向的 struct sockaddr 的實際大小。
recvfrom() 返回它接收到的字節(jié)數,如果發(fā)生了錯誤,它就返回 – 1,全局變量 errno 存儲了錯誤代碼。
如果一個信息大得緩沖區(qū)都放不下,那么附加信息將被砍掉。該調用可以立即返回,也可以**的 等待。這取決于你把 flags 設置成什么類型。你甚至可以設置超時(timeout)值。
在說明書(man pages)中可以找到 recvfrom 的更多信息。
注意:如果你使用 cnnect() 連接到了一個數據報套接字的服務器程序上,那么你就可以使用
send() 和 recv() 函數來傳輸你的數據。不要以為你在使用一個流式的套接字,你所使用的仍然是一個用戶數據報的套接字,只不過套接字界面在 send() 和 recv()的時候自動幫助你加上了目標地址, 目標端口的信息。
8.5.8 close()和 shutdown()函數
程序進行網絡傳輸完畢后,你需要關閉這個套接字描述符所表示的連接。實現這個非常簡單,只需 要使用標準的關閉文件的函數:close()。
使 用 方 法 : close(sockfd);
執(zhí)行 close()之后,套接字將不會在允許進行讀**作和寫**作。任何有關對套接字描述符進行讀和寫的**作都會接收到一個錯誤。
如果你想對網絡套接字的關閉進行進一步的**作的話,你可以使用函數 shutdown()。它允許你進行單向的關閉**作,或是全部禁止掉。
shutdown()的聲明為:
#include <sys/socket.h>
int shutdown(int sockfd, int how); 它的參數含義如下:
· sockfd 是一個你所想關閉的套接字描述符。
· how 可以取下面的值。0 表示不允許以后數據的接收**;1 表示不允許以后數據的發(fā)送**作;
2 表示和 close() 一樣,不允許以后的任何**作(包括接收,發(fā)送數據)。
shutdown() 如果執(zhí)行成功將返回 0,如果在調用過程中發(fā)生了錯誤,它將返回–1,全局變量 errno中存儲了錯誤代碼。
如果你在一個未連接的數據報套接字上使用 shutdown() 函數(還記得可以對數據報套接字 UDP 進行 connect() **作嗎?),它將什么也不做。
8.5.9 setsockopt()和 getsockopt()函數
Linux 所提供的 socket 庫含有一個錯誤(bug)。此錯誤表現為你不能為一個套接字重新啟用同一個端口號,即使在你正常關閉該套接字以后。例如,比方說,你編寫一個服務器在一個套接字上等待的 程序。服務器打開套接字并在其上偵聽是沒有問題的。無論如何,總有一些原因(不管是正常還是非正 常的結束程序)使你的程序需要重新啟動。然而重啟動后你就不能把它綁定在原來那個端口上了。從
bind() 系統(tǒng)調用返回的錯誤代碼總是報告說你試圖連接的端口已經被別的進程所綁定。
問題就是 Linux 內核在一個綁定套接字的進程結束后從不把端口標記為未用。在大多數
Linux/UNIX 系統(tǒng)中,端口可以被一個進程重復使用,甚至可以被其它進程使用。
在 Linux 中繞開這個問題的辦法是,當套接字已經打開但尚未有連接的時候用 setsockopt() 系統(tǒng)調用在其上設定選項(options)。而 getsockopt() 可以從給定的套接字取得選項。
這里是這些調用的語法:
#include<sys/types.h>
#include<sys/socket.h>
int getsockopt(int sockfd, int level, int name,
char *value, int *optlen); int setsockopt(int sockfd, int level, int name,
char *value, int *optlen); 下面是兩個調用的參數說明:
· sockfd 必須是一個已打開的套接字。
· level 是函數所使用的協(xié)議標準(protocol level) (TCP/IP 協(xié)議使用
PPROTO_TCP,套接字標準的選項實用 SOL_SOCKET)。
· name 選項在套接字說明書中(man page)有詳細說明。
· value 指向為 getsockopt()函數所獲取的值,setsockopt()函數所設置的值的地址。
· optlen 指針指向一個整數,該整數包含參數以字節(jié)計算的長度。
現在我們再回到 Linux 的錯誤上來.當你打開一個套接字時必須同時用下面的代碼段來調用
setsockopt() 函數:
/* 設定參數數值 */
opt = 1; len = sizeof(opt);
/* 設置套接字屬性 */ setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);
setsockopt()函數還有很多其他用法,請參考幫助頁(man pages)。
8.5.10 getpeername() 函數
這個函數可以取得一個已經連接上的套接字的遠程信息(比如 IP 地址和端口),告訴你在遠程和你連接的到底是誰。
它的聲明為:
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); 下面是參數說明:
· sockfd 是你想取得遠程信息的那個套接字描述符。
· addr 是一個指向 struct sockaddr (或是 struct sockaddr_in)的指針。
· addrlen 是一個指向 int 的指針,應該賦于 sizeof(struct sockaddr)的大小。
如果在函數執(zhí)行過程中出現了錯誤,函數將返回 –1,并且錯誤代碼儲存在全局變量 errno 中。當你擁有了遠程連接用戶的 IP 地址,你就可以使用 inet_ntoa() 或 gethostbyaddr() 來輸出
信息或是做進一步的處理。
8.5.11 gethostname()函數
gethostname() 函數可以取得本地主機的信息.它比 getpeername() 要容易使用一些。
它返回正在執(zhí)行它的計算機的名字。返回的這個名字可以被 gethostbyname() 函數使用,由此可以得到本地主機的 IP 地址。
下面是它的聲明:
#include <unistd.h>
int gethostname(char *hostname, size_t size); 參數說明如下:
· hostname 是一個指向字符數組的指針,當函數返回的時候,它里面的數據就是本地的主機的名字。
· size 是 hostname 指向的數組的長度。
函數如果成功執(zhí)行,它返回 0,如果出現錯誤,則返回–1,全局變量 errno 中存儲著錯誤代碼。
拓展知識:
原創(chuàng)文章,作者:九賢生活小編,如若轉載,請注明出處:http://m.cxzzxj.cn/49869.html