參考網址:https://blog.csdn.net/Datura_Metel/article/details/79900395
https://www.2cto.com/kf/201804/736240.html
https://blog.csdn.net/m0_37947204/article/details/80489431
https://blog.csdn.net/jinmie0193/article/details/78951055
哈工大相關課程ppt
小弟我學識淺薄,理解不到位之處請各位海涵,另請大哥提點提點!小弟我感激不盡!
目標是一篇搞懂socket編程的所有api,一篇進行實戰演練。
首先解決下面幾個問題:
1.對socket編程的理解:

也即是:調用一些操作系統提供的api,來與應用進程進行交互。
1.微軟公司在其操作系統中采用了套接字接口 API ,形成了一個稍有不同的 API,並稱之為 Windows Socket Interface,WINSOCK。
2.Berkeley UNIX 操作系統定義了一種 API,稱為 套接字接口(socket interface),簡稱套接字( socket)。
3.AT&T 為其 UNIX 系統 V 定義了一種 API,簡寫 為 TLI (Transport Layer Interface)。
這里使用的是WINSOCK
如何標識對外的通信端點呢? IP地址+端口號
操作系統/進程如何管理套接字(對內)?套接字描述符(socket descriptor)是一個小整數 。
類似於文件的抽象
當應用進程創建套接字時,操作系統分配一個數據結構存儲該套接字相關信息
返回套接字描述符

使用TCP/IP協議簇的網絡應用程序聲明端點地址變量時,使用結構 sockaddr_in.
定義結構sockaddr_in:
/* * Socket address, internet style. */ struct sockaddr_in { short sin_family;/*地址族(TCP/IP:AF_INET) */ u_short sin_port;/*端口號 */ struct in_addr sin_addr;/*IP地址 */ char sin_zero[8];/*未用(置0) */ };

WSAStartup

使用Socket的應用程序在使用Socket之前必須首先調用 WSAStartup函數
兩個參數:
第一個參數指明程序請求使用的WinSock版本,其中高位字節指明副版本、低位字節指明主版本.
十六進制整數,例如0x102表示2.1版
第二個參數返回實際的WinSock的版本信息
• 指向WSADATA結構的指針
示例代碼如下:使用2.1版本的WinSock的程序代碼段 :
wVersionRequested = MAKEWORD( 2, 1 ); //創建字,高字節是2,低字節是1,表示版本2.1
err = WSAStartup( wVersionRequested, &wsaData ); //返回實際的WinSock的版本信息
WSACleanup

應用程序在完成對請求的Socket庫的使用, 最后要調用WSACleanup函數解除與Socket庫的綁定並釋放Socket庫所占用的系統資源
不清理的話,在運行codeblocks的時候,常常可能會發生id.exe被占用錯誤。無法運行,需要去管理器找進程關掉才可以繼續運行。
此類型錯誤處理辦法詳見我其他博文。
socket

創建套接字,操作系統返回套接字描述符(sd)
第一個參數(協議族): protofamily = PF_INET(TCP/IP)
第二個參數(套接字類型):
type = SOCK_STREAM,SOCK_DGRAM or SOCK_RAW(TCP/IP)
第三個參數(協議號):0為默認
例:創建一個流套接字的代碼段
struct protoent *p; p=getprotobyname("tcp"); SOCKET sd=socket(PF_INET,SOCK_STREAM,p->p_proto);


TCP:可靠、面向連接、字節流傳輸、點對點
UDP:不可靠、無連接、數據報傳輸
Closesocket

注意c語言中,如果你不管的話,就會一直保留着資源。。
關閉一個描述符為sd的套接字
如果多個進程共享一個套接字,調用closesocket 將套接字引用計數減1,減至0才關閉
一個進程中的多線程對一個套接字的使用無計數
如果進程中的一個線程調用closesocket將一個套接字 關閉,該進程中的其他線程也將不能訪問該套接字
返回值:
0:成功
SOCKET_ERROR:失敗
bind

綁定套接字的本地端點地址
IP地址+端口號
參數:
套接字描述符(sd)
端點地址(localaddr) • 結構sockaddr_in
客戶程序一般不必調用bind函數
服務器端?
熟知端口號:
IP地址? 地址通配符:INADDR_ANY

參考網站:https://www.cnblogs.com/ok-lanyan/articles/2634242.html
http://blog.chinaunix.net/uid-23193900-id-3199173.html
有連接的socket客戶端通過調用Connect函數在socket數據結構中保存本地和遠端信息,無須調用bind(),因為這種情況下只需知道目的機器的IP地址,而客戶通過哪個端口與服務器建立連接並不需要關心,socket執行體為你的程序自動選擇一個未被占用的端口,並通知你的程序數據什么時候打開端口。
1.需要在建連前就知道端口的話,需要 bind
2.需要通過指定的端口來通訊的話,需要 bind
本來用的是TCP,客戶端就不用綁定端口了,綁定之后只能運行一個client的程序屬於自己人為設定的障礙,而從服務器那邊得到的客戶機連接端口號(是系統自動分配的)與這邊客戶機綁定的端口號根本是不相關的,所以客戶端綁定也就失去了意義。
如果bind了,就綁定了服務器的端口,也就導致了綁定之后只能運行一個client的程序。
listen

置服務器端的流套接字處於監聽狀態
僅服務器端調用
僅用於面向連接的流套接字
設置連接請求隊列大小(queuesize)
返回值:
0:成功
SOCKET_ERROR:失敗

connect

客戶程序調用connect函數來使客戶套接字(sd)與特定計算機 的特定端口(saddr)的套接字(服務)進行連接
僅用於客戶端
可用於TCP客戶端也可以用於 UDP客戶端
TCP客戶端:建立TCP連接
UDP客戶端:指定服務器端點地址

accept

服務程序調用accept函數從 處於監聽狀態的流套接字sd 的客戶連接請求隊列中取出排在最前的一個客戶請求, 並且創建一個新的套接字來與客戶套接字創建連接通道
僅用於TCP套接字
僅用於服務器
利用新創建的套接字 (newsock)與客戶通信

send, sendto

send函數TCP套接字(客戶與服務器)或調用了 connect函數的UDP客戶端套接字
假如調用了connect了,自然就知道了特定端口套接字了,無需再聲明destaddr了。
sendto函數用於UDP服務器端套接字與未調用 connect函數的UDP客戶端套接字
假如沒有調用connect,自然就不知道特定端口套接字,需要聲明destaddr,addrlen!!!
我們這里需要指出一點:
由於UDP是無連接的,你使用connect()進行了連接,他也只是發一個:/*我要開始連接了!*/給服務器,然后就開始自顧自地用sendto()或者send()發自己地請求。connect()中,調用TCP返回成功是真的建立了連接,但是UDP並不一定真正建立了連接!!!!
UDP就像一個任性的人,他說,我要開始做某事了,於是就不論他人是否合作,自己干了起來。
recv, recvfrom

recv函數從TCP連接的另一端接收數據,或者從調用了connect函數的UDP客戶端套接字接收服務 器發來的數據
假如調用了connect了,自然就知道了特定端口套接字了,無需再聲明destaddr了。
recvfrom函數用於從UDP服務器端套接字與未調 用connect函數的UDP客戶端套接字接收對端數據
類似同上。
setsockopt, getsockopt

setsockopt()函數用來設置套接字sd的選項參數
getsockopt()函數用於獲取任意類型、任意狀態套 接口的選項當前值,並把結果存入optval
小結:


一個容易忽略的問題:進行網絡傳輸機器兩端的“大端機器”和“小端機器“的處理:

網絡應用的Socket API(TCP)調用基本流程

到這里,主要使用的API就說完了。
