socket 通信关于bind那点事
結論:
1、采用TCP通信時,客戶端不需要bind()他自己的IP和端口號,而服務器必須要bind()自己本機的IP和端口號;
2、若采用UDP通信時(這里是有客戶端和服務器之分才這么說的,若是指定特定端口的UDP對等通信則不一樣了),客戶端也可以不需要bind()他自己的IP和端口號,而服務器需要bind自己IP地址和端口號;
原因:
1、
因為服務器是時時在監聽有沒有客戶端的連接,如果服務器不綁定IP和端口的話,客戶端上線的時候怎么連到服務器呢,所以服務器要綁定IP和端口,而客戶端就不需要了,客戶端上線是主動向服務器發出請求的,因為服務器已經綁定了IP和端口,所以客戶端上線的就向這個IP和端口發出請求,這時因為客戶開始發數據了(發上線請求),系統就給客戶端分配一個隨機端口,這個端口和客戶端的IP會隨著上線請求一起發給服務器,服務收到上線請求后就可以從中獲起發此請求的客戶的IP和端口,接下來服務器就可以利用獲起的IP和端口給客戶端回應消息了。
2、采用UDP通信
1)若有客戶端和服務器之分的程序,創建sock后即可在該socket上用recvfrom/sendto方法發送接受數據了,因為客戶端只需要用sendto發送數據到指定的地址,當然若是bind了,程序也沒什么問題,區別就是系統用默認自動bind()指定你自己的socket參數地址(特別是在指定特定端口的UDP對等通信)只是這種情況沒有這樣用的。
那UDP服務器是怎么知道客戶端的IP地址和UDP端口?
一般來說有兩種方式:
一種是客戶端發消息顯式地告訴服務器IP地址和端口,消息內容就包括IP地址和UDP端口。
另外一種就是隱式的,服務器從收到的包的頭部中得到包的源IP地址和端口。
2)若是沒有客戶端和服務器之分的程序,即自己指定特定端口的UDP對等通信,則客戶端和服務器都需要bind()IP地址和端口了。
通常udp服務端根本不需要知道客戶端的socket,它直接建立一個socket用于發送即可,udp通信的關鍵只在于IP和端口。
多個客戶端如果需要點到點分發,必須給服務端socket循環設置每個客戶端的IP并發出,但更常用的是廣播分發,服務端socket設定一個X.X.X.255的廣播地址并始終向它發送,每個客戶端建立的socket只需要綁定這個廣播地址便可以收到。
?
?
?
客戶端用不用bind 的區別
無連接的socket的客戶端和服務端以及面向連接socket的服務端通過調用bind函數來配置本地信息。使用bind函數時,通過將my_addr.sin_port置為0,函數會自動為你選擇一個未占用的端口來使用。? Bind()函數在成功被調用時返回0;出現錯誤時返回"-1"并將errno置為相應的錯誤號。需要注意的是,在調用bind函數時一般不要將端口號置為小于1024的值,因為1到1024是保留端口號,你可以選擇大于1024中的任何一個沒有被占用的端口號。
?有連接的socket客戶端通過調用Connect函數在socket數據結構中保存本地和遠端信息,無須調用bind(),因為這種情況下只需知道目的機器的IP地址,而客戶通過哪個端口與服務器建立連接并不需要關心,socket執行體為你的程序自動選擇一個未被占用的端口,并通知你的程序數據什么時候打開端口。(當然也有特殊情況,linux系統中rlogin命令應當調用bind函數綁定一個未用的保留端口號,還有當客戶端需要用指定的網絡設備接口和端口號進行通信等等) 總之:
1.需要在建連前就知道端口的話,需要?bind?
2.需要通過指定的端口來通訊的話,需要?bind ?
具體到上面那兩個程序,本來用的是TCP,客戶端就不用綁定端口了,綁定之后只能運行一個client 的程序,是屬于自己程序中人為設定的障礙,而從服務器那邊得到的客戶機連接端口號(是系統自動分配的)與這邊客戶機綁定的端口號根本是不相關的,所以客戶 綁定也就失去了意義。 注意: 一個端口可以用于多個連接(比如多個客戶端連接服務器的同一端口)。但是在同一個操作系統上,即服務器和客戶端都是本機上,多個客戶端去連接服務器,只有第一個客戶端的連接會被接收,第二個客戶端的連接請求不會被接收。 首先,服務器和客戶端都可以bind,bind并不是服務器的專利。 客戶端進程bind端口:?由進程選擇一個端口去連服務器,(如果默認情況下,調用bind函數時,內核指定的端口是同一個,那么運行多個調用了bind 的client?程序,會出現端口被占用的錯誤)注意這里的端口是客戶端的端口。如果不分配就表示交給內核去選擇一個可用端口。 客戶端進程bind IP地址:相當于為發送出去的IP數據報分配了源IP地址,但交給進程分配IP地址的時候(就是這樣寫明了bind IP地址的時候)這個IP地址必須是主機的一個接口,不能分配一個不存在的IP。如果不分配就表示由內核根據所用的輸出接口來選擇源IP地址。
? 一般情況下客戶端是不用調用bind函數的,一切都交給內核搞定!
?服務端進程bind端口:基本是必須要做的事情,比如一個服務器啟動時(比如freebsd),它會一個一個的捆綁眾所周知的端口來提供服務,同樣,如果bind了一個端口就表示我這個服務器會在這個端口提供一些“特殊服務”。 ?服務端進程bind IP地址:目的是限制了服務端進程創建的socket只接受那些目的地為此IP地址的客戶鏈接,一般一個服務器程序里都有 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 只是針對IP4,IP6代碼不太一樣 這樣一句話,意思就是:我不指定客戶端的IP,隨便連,來者不拒!
總之只要你bind時候沒有指定哪一項(置為0),內核會幫你選擇。
?
=======================================================
?
客戶端調用bind 的作用及UDP客戶端調用connect 的問題
在水木上看到一個關于在客戶端調用bind的討論,
http://www.newsmth.net/bbstcon.php?board=NetPRG&gid=40783如果不調用bind,則客戶端在向外發包時,會由系統自己決定使用的接口的源端口,而調用bind則可以指定相應的參數。
另外有個哥們提到“果是udp,使用bind以后,可以不使用sendto/recvform函數,而直接用write/read函數了,少去了寫一個參數。”,這應該是調用connect后的效果。
關于UDP中客戶端調用connect的好處,還有一個作用是能夠捕獲錯誤。 由于UDP是無連接的,connect在調用時其實沒有向外發包,只是在協議棧中記錄了該狀態,應該是生成了一個類似TCB的結構。之后如果發生網絡異常,比如對端不可達,客戶端在往對端寫數據后,本機會收到一個ICMP回應,則回來的ICMP不可達的響應能夠被協議棧處理,通知客戶端進程;當客戶端再次對該fd進行操作時,比如讀數據時,read等調用會返回一個錯誤。而不調用connect時,對于返回的ICMP響應,協議棧不知道該傳遞給上層的哪個應用,所以客戶端進程中捕獲不到相應的錯誤。 在兩種情況下,write或者sendto操作都是把數據放到協議棧的發送隊列之后就返回成功,而相應的ICMP回應則要等數據到達對端后才能返回,所以通常這種情況叫做“異步錯誤”。
使用下列代碼進行驗證: [cpp]?view plaincopyprint?
########################實驗1,客戶端進行connect###################### 客戶端執行: mt@ubuntu:~/code$ gcc -o udpclient_connect?-DUDP_CONNECT?udpclient.c mt@ubuntu:~/code$ ./udpclient_connect 192.168.0.1 abcd write over read error: Connection refused
在另一窗口抓包: mt@ubuntu:~$ sudo tcpdump -i eth0 port 8888 or icmp -v tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 21:49:40.300735 IP (tos 0x0, ttl 64, id 20973, offset 0, flags [DF], proto UDP (17), length 33) localhost.42774 > localhost.8888: UDP, length 5 21:49:40.303965 IP (tos 0x0, ttl 64, id 22696, offset 0, flags [none], proto ICMP (1), length 56) localhost > localhost: ICMP localhost udp port 8888 unreachable, length 36 IP (tos 0x0, ttl 64, id 20973, offset 0, flags [DF], proto UDP (17), length 33) localhost.42774 > localhost.8888: UDP, length 5
可以看到,客戶端發送數據之后,收到了ICMP不可達的回應,此時客戶端進程的read()操作返回了錯誤,通過perror打印出來的錯誤信息為:Connection refused ##########################實驗1 結束###################################
###########################實驗2,客戶端不進行connect############### mt@ubuntu:~/code$ gcc -o udpclient udpclient.c mt@ubuntu:~/code$ ./udpclient 192.168.0.1 abcd write over
在另一窗口抓包: mt@ubuntu:~$ sudo tcpdump -i eth0 port 8888 or icmp -v tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 22:14:23.863178 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 33) localhost.46642 > localhost.8888: UDP, length 5 22:14:23.864000 IP (tos 0x0, ttl 64, id 22730, offset 0, flags [none], proto ICMP (1), length 56) localhost > localhost: ICMP localhost udp port 8888 unreachable, length 36 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 33) localhost.46642 > localhost.8888: UDP, length 5
盡管有ICMP回應返回,但客戶端沒有捕獲到該錯誤,此時阻塞在了read調用上。 ##############################實驗2 結束############################
另外,調用connect之后,會發現應用程序只會對調用了connect的fd進行相應的操作,如果它同時監聽在某fd上,則不會響應該監聽fd上的數據。比如參考資料2中提到一個程序先用UDP監聽在機器B的9000上,同時用udp connect到另一臺機器A的8000端口,結果發現使用其他機器往機器B的9000端口發送數據時,它不會做出響應。
參考: [1 ]http://baike.baidu.com/view/30509.htm [2]?http://hi.baidu.com/rwen2012/blog/item/523c4bf4f63327e67709d723.html? [3]www.cs.rpi.edu/~hollingd/netprog/notes/udp/udp.pdf
總結
以上是生活随笔為你收集整理的socket 通信关于bind那点事的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于WSAAsyncSelect模型实现
- 下一篇: recv send 阻塞和非阻塞