socket() 函式用來建立套接字,確定套接字的各種屬性,然後伺服器端要用 bind() 函式將套接字與特定的ip位址和埠繫結起來,只有這樣,流經該ip位址和埠的資料才能交給套接字處理;而客戶端要用 connect() 函式建立連線。bind() 函式的原型為:
int bind(int sock, struct sockaddr *addr, socklen_t addrlen); //linuxint bind(socket sock, const struct sockaddr *addr, int addrlen); //windows
下面以linux為例進行講解,windows與此類似。sock 為 socket 檔案描述符,addr 為 sockaddr 結構體變數的指標,addrlen 為 addr 變數的大小,可由 sizeof() 計算得出。
下面的**,將建立的套接字與ip位址 127.0.0.1、埠 1234 繫結:
//建立套接字
int serv_sock =
socket
(af_inet, sock_stream, ipproto_tcp);
//建立sockaddr_in結構體變數
struct
sockaddr_in serv_addr;
memset
(&serv_addr,0,
sizeof
(serv_addr));
//每個位元組都用0填充
serv_addr.sin_family = af_inet;
//使用ipv4位址
serv_addr.sin_addr.s_addr =
inet_addr
("127.0.0.1"
);//具體的ip位址
serv_addr.sin_port =
htons
(1234
);//埠
//將套接字和ip、埠繫結
bind
(serv_sock,
(struct
sockaddr
*)&serv_addr,
sizeof
(serv_addr));
這裡我們使用 sockaddr_in 結構體,然後再強制轉換為 sockaddr 型別,後邊會講解為什麼這樣做。
sockaddr_in 結構體
接下來不妨先看一下 sockaddr_in 結構體,它的成員變數如下:
struct
sockaddr_in
;
1) sin_family 和 socket() 的第乙個引數的含義相同,取值也要保持一致。
2) sin_prot 為埠號。uint16_t 的長度為兩個位元組,理論上埠號的取值範圍為 0~65536,但 0~1023 的埠一般由系統分配給特定的服務程式,例如 web 服務的埠號為 80,ftp 服務的埠號為 21,所以我們的程式要盡量在 1024~65536 之間分配埠號。
埠號需要用 htons() 函式轉換,後面會講解為什麼。
3) sin_addr 是 struct in_addr 結構體型別的變數,下面會詳細講解。
4) sin_zero[8] 是多餘的8個位元組,沒有用,一般使用 memset() 函式填充為 0。上面的**中,先用 memset() 將結構體的全部位元組填充為 0,再給前3個成員賦值,剩下的 sin_zero 自然就是 0 了。
in_addr 結構體
sockaddr_in 的第3個成員是 in_addr 型別的結構體,該結構體只包含乙個成員,如下所示:
struct
in_addr
;
in_addr_t 在標頭檔案 中定義,等價於 unsigned long,長度為4個位元組。也就是說,s_addr 是乙個整數,而ip位址是乙個字串,所以需要 inet_addr() 函式進行轉換,例如:
unsigned
long ip =
inet_addr
("127.0.0.1"
);printf
("%ld\n"
, ip);
執行結果:
** sockaddr_in 結構體
為什麼要搞這麼複雜,結構體中巢狀結構體,而不用 sockaddr_in 的乙個成員變數來指明ip位址呢?socket() 函式的第乙個引數已經指明了位址型別,為什麼在 sockaddr_in 結構體中還要再說明一次呢,這不是囉嗦嗎?
這些繁瑣的細節確實給初學者帶來了一定的障礙,我想,這或許是歷史原因吧,後面的介面總要相容前面的**。各位讀者一定要有耐心,暫時不理解沒有關係,根據教程中的**「照貓畫虎」即可,時間久了自然會接受。
為什麼使用 sockaddr_in 而不使用 sockaddr
bind() 第二個引數的型別為 sockaddr,而**中卻使用 sockaddr_in,然後再強制轉換為 sockaddr,這是為什麼呢?
sockaddr 結構體的定義如下:
struct
sockaddr
;
下圖是 sockaddr 與 sockaddr_in 的對比(括號中的數字表示所占用的位元組數):
sockaddr 和 sockaddr_in 的長度相同,都是16位元組,只是將ip位址和埠號合併到一起,用乙個成員 sa_data 表示。要想給 sa_data 賦值,必須同時指明ip位址和埠號,例如」127.0.0.1:80「,遺憾的是,沒有相關函式將這個字串轉換成需要的形式,也就很難給 sockaddr 型別的變數賦值,所以使用 sockaddr_in 來代替。這兩個結構體的長度相同,強制轉換型別時不會丟失位元組,也沒有多餘的位元組。
可以認為,sockaddr 是一種通用的結構體,可以用來儲存多種型別的ip位址和埠號,而 sockaddr_in 是專門用來儲存 ipv4 位址的結構體。
另外還有 sockaddr_in6,用來儲存 ipv6 位址,它的定義如下:
struct
sockaddr_in6
;
正是由於通用結構體 sockaddr 使用不便,才針對不同的位址型別定義了不同的結構體。
connect() 函式用來建立連線,它的原型為:
int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen); //linux各個引數的說明和 bind() 相同,不再贅述。int connect(socket sock, const struct sockaddr *serv_addr, int addrlen); //windows
underscore的bind和bindAll方法
bind方法和bindall方法都是用來設定函式的this值的,區別是呼叫方式不同。var xiaoming bind var func bind xiaoming,xiaoming.say func i am xiaoming bindall bindall xiaoming,say var fu...
linux函式 connect的使用和注意事項
在linux中的socket程式設計中,建立網路連線是通過connect函式來完成的,函式原型為 int connect int sockfd,struct sockaddr serv addr,int addrlen addrlen 為傳入引數sockaddr結構體的大小,該引數實際應該是乙個傳出...
使用 Bind 方法
使用 bind 方法一樣來檢索資料繫結欄位的值,但當資料可以被修改時,還是要使用 bind 方法。在 asp.net 中,資料繫結控制項 如 gridview detailsview 和 formview 控制項 可自動使用資料來源控制項的更新 刪除和插入操作。例如,如果已為資料來源控制項定義了 s...