深入淺出的《網路socket程式設計指南》2
socket()函式
我想我不能再不提這個了-下面我將討論一下socket()系統呼叫。
下面是詳細介紹:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
但是它們的引數是什麼? 首先,domain 應該設定成 "AF_INET",就 象上面的資料結構struct sockaddr_in 中一樣。然後,引數 type 告訴核心 是 SOCK_STREAM 型別還是 SOCK_DGRAM 型別。最後,把 protocol 設定為 "0"。(注意:有很多種 domain、type,我不可能一一列出了,請看 socket() 的 man幫助。當然,還有一個"更好"的方式去得到 protocol。同 時請查閱 getprotobyname() 的 man 幫助。)
socket() 只是返回你以後在系統呼叫種可能用到的 socket 描述符,或 者在錯誤的時候返回-1。全域性變數 errno 中將儲存返回的錯誤值。(請參考 perror() 的 man 幫助。)
--------------------------------------------------------------------------------
bind()函式
一旦你有一個套接字,你可能要將套接字和機器上的一定的埠關聯 起來。(如果你想用listen()來偵聽一定埠的資料,這是必要一步--MUD 告 訴你說用命令 "telnet x.y.z 6969"。)如果你只想用 connect(),那麼這個步 驟沒有必要。但是無論如何,請繼續讀下去。
這裡是系統呼叫 bind() 的大概:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
sockfd 是呼叫 socket 返回的檔案描述符。my_addr 是指向資料結構 struct sockaddr 的指標,它儲存你的地址(即埠和 IP 地址) 資訊。 addrlen 設定為 sizeof(struct sockaddr)。
簡單得很不是嗎? 再看看例子:
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MYPORT 3490
main()
{
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*需要錯誤檢查 */
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(MYPORT); /* short, network byte order */
my_addr.sin_addr.s_addr = inet_addr("132.241.5.10");
bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */
/* don't forget your error checking for bind(): */
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
.
.
.
這裡也有要注意的幾件事情。my_addr.sin_port 是網路位元組順序, my_addr.sin_addr.s_addr 也是的。另外要注意到的事情是因系統的不同, 包含的標頭檔案也不盡相同,請查閱本地的 man 幫助檔案。
在 bind() 主題中最後要說的話是,在處理自己的 IP 地址和/或埠的 時候,有些工作是可以自動處理的。
my_addr.sin_port = 0; /* 隨機選擇一個沒有使用的埠 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 使用自己的IP地址 */
通過將0賦給 my_addr.sin_port,你告訴 bind() 自己選擇合適的端 口。同樣,將 my_addr.sin_addr.s_addr 設定為 INADDR_ANY,你告訴 它自動填上它所執行的機器的 IP 地址。
如果你一向小心謹慎,那麼你可能注意到我沒有將 INADDR_ANY 轉 換為網路位元組順序!這是因為我知道內部的東西:INADDR_ANY 實際上就 是 0!即使你改變位元組的順序,0依然是0。但是完美主義者說應該處處一 致,INADDR_ANY或許是12呢?你的程式碼就不能工作了,那麼就看下面 的程式碼:
my_addr.sin_port = htons(0); /* 隨機選擇一個沒有使用的埠 */
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 使用自己的IP地址 */
你或許不相信,上面的程式碼將可以隨便移植。我只是想指出,既然你 所遇到的程式不會都執行使用htonl的INADDR_ANY。
bind() 在錯誤的時候依然是返回-1,並且設定全域性錯誤變數errno。
在你呼叫 bind() 的時候,你要小心的另一件事情是:不要採用小於 1024的埠號。所有小於1024的埠號都被系統保留!你可以選擇從1024 到65535的埠(如果它們沒有被別的程式使用的話)。
你要注意的另外一件小事是:有時候你根本不需要呼叫它。如果你使 用 connect() 來和遠端機器進行通訊,你不需要關心你的本地埠號(就象 你在使用 telnet 的時候),你只要簡單的呼叫 connect() 就可以了,它會檢 查套接字是否繫結埠,如果沒有,它會自己繫結一個沒有使用的本地埠。
--------------------------------------------------------------------------------
connect()程式
現在我們假設你是個 telnet 程式。你的使用者命令你得到套接字的檔案 描述符。你聽從命令呼叫了socket()。下一步,你的使用者告訴你通過埠 23(標準 telnet 埠)連線到"132.241.5.10"。你該怎麼做呢? 幸運的是,你正在閱讀 connect()--如何連線到遠端主機這一章。你可 不想讓你的使用者失望。
connect() 系統呼叫是這樣的:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd 是系統呼叫 socket() 返回的套接字檔案描述符。serv_addr 是 儲存著目的地埠和 IP 地址的資料結構 struct sockaddr。addrlen 設定 為 sizeof(struct sockaddr)。
想知道得更多嗎?讓我們來看個例子:
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define DEST_IP "132.241.5.10"
#define DEST_PORT 23
main()
{
int sockfd;
struct sockaddr_in dest_addr; /* 目的地址*/
sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 錯誤檢查 */
dest_addr.sin_family = AF_INET; /* host byte order */
dest_addr.sin_port = htons(DEST_PORT); /* short, network byte order */
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
bzero(&(dest_addr.sin_zero),; /* zero the rest of the struct */
/* don't forget to error check the connect()! */
connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));
.
.
.
再一次,你應該檢查 connect() 的返回值--它在錯誤的時候返回-1,並 設定全域性錯誤變數 errno。
同時,你可能看到,我沒有呼叫 bind()。因為我不在乎本地的埠號。 我只關心我要去那。核心將為我選擇一個合適的埠號,而我們所連線的 地方也自動地獲得這些資訊。一切都不用擔心。