Socket 編程
Socket 編程-應用編程介面(API)
網路程式設計介面

應用編程介面 API(Application Programming Interface)
應用編程介面 API:就是應用行程的控制權和作業系統的控制權進行轉換的一個系統呼叫介面.

幾種典型的應用編程介面:
- Berkeley UNIX 作業系統定義了一種 API,稱為套接字介面(socket interface),簡稱套接字(socket),
- 微軟公司在其作業系統中采用了套接字介面 API,形成了一個稍有不同的 API,并稱之為 Windows Socket Interface,WINSOCK,
- AT&T 為其 UNIX 系統 V 定義了一種 API,簡寫為 TLI(Transport Layer Interface)
Socket 編程-Socket API 概述
- 最初設計:面向 BSD UNIX-Berkley,面向 TCP/IP 協議堆疊介面
- 目前:事實上的工業標準,絕大多數作業系統都支持
- Internet 網路應用最典型的 API 介面
- 通信模型:客戶/服務器(C/S)
- 應用行程間通信的抽象機制


標識通信端點(對外):IP 地址+埠號
作業系統/行程如何管理套接字(對內)?答:套接字描述符(socket descriptor),小整數
Socket 抽象
類似于檔案的抽象,當應用行程創建套接字時,作業系統分配一個資料結構存盤該套接字相關資訊,回傳套接字描述符

地址結構
已定義結構 sockaddr_in:
structsockaddr_in
{
u_charsin_len; /*地址長度*/
u_charsin_family; /*地址族(TCP/IP:AF_INET)*/
u_shortsin_port; /*埠號*/
structin_addrsin_addr;/*IP地址*/
char sin_zero[8]; /*未用(置0)*/
}
使用 TCP/IP 協議簇的網路應用程式宣告端點地址變數時,使用結構 sockaddr_in
Socket 編程-Socket API 函式
Socket API 函式(WinSock)

//WSAStartup
intWSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
//WSACleanup
intWSACleanup(void);
使用 Socket 的應用程式在使用 Socket 之前必須首先呼叫 WSAStartup 函式
- 第一個引數指明程式請求使用的 WinSock 版本,其中高位位元組指明副版本、低位位元組指明主版本.
- 十六進制整數,例如 0x102 表示 2.1 版
- 第二個引數回傳實際的 WinSock 的版本資訊
- 指向 WSADATA 結構的指標
//使用2.1版本的WinSock的程式代碼段
wVersionRequested= MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData);
應用程式在完成對請求的 Socket 庫的使用,最后要呼叫 WSACleanup 函式,解除與 Socket 庫的系結,釋放 Socket 庫所占用的系統資源
sd= socket(protofamily,type,proto);
創建套接字,作業系統回傳套接字描述符(sd),第一個引數(協議族): protofamily= PF_INET(TCP/IP),第二個引數(套接字型別):type = SOCK_STREAM,SOCK_DGRAM orSOCK_RAW(TCP/IP,第三個引數(協議號):0 為默認
//創建一個流套接字的代碼段
structprotoent*p;
p=getprotobyname("tcp");
SOCKET sd=socket(PF_INET,SOCK_STREAM,p->p_proto);

Socket 面向 TCP/IP 的服務型別

- TCP:可靠、面向連接、位元組流傳輸、點對點
- UDP:不可靠、無連接、資料報傳輸
int closesocket(SOCKET sd);
關閉一個描述符為 sd 的套接字,如果多個行程共享一個套接字,呼叫 closesocket 將套接字參考計數減 1,減至 0 才關閉,一個行程中的多執行緒對一個套接字的使用無計數,如果行程中的一個執行緒呼叫 closesocket 將一個套接字關閉,該行程中的其他執行緒也將不能訪問該套接字,回傳值:0:成功,SOCKET_ERROR:失敗
int bind(sd,localaddr,addrlen);
系結套接字的本地端點地址:IP 地址+埠號
引數:套接字描述符(sd),端點地址(localaddr)
客戶程式一般不必呼叫 bind 函式
服務器端:熟知埠號,IP 地址


int listen(sd,queuesize);
置服務器端的流套接字處于監聽狀態,僅服務器端呼叫,僅用于面向連接的流套接字
設定連接請求佇列大小(queuesize)
回傳值:0:成功 SOCKET_ERROR:失敗

connect(sd,saddr,saddrlen);
客戶程式呼叫 connect 函式來使客戶套接字(sd)與特定計算機的特定埠(saddr)的套接字(服務)進行連接,僅用于客戶端,可用于 TCP 客戶端也可以用于 UDP 客戶端,TCP 客戶端:建立 TCP 連接,UDP 客戶端:指定服務器端點地址

newsock= accept(sd,caddr,caddrlen);
服務程式呼叫 accept 函式從處于監聽狀態的流套接字 sd 的客戶連接請求佇列中取出排在最前的一個客戶請求,并且創建一個新的套接字來與客戶套接字創建連接通道,僅用于 TCP 套接字,僅用于服務器
利用新創建的套接字(newsock)與客戶通信

send(sd,*buf,len,flags);
send 函式 TCP 套接字(客戶與服務器)或呼叫了 connect 函式的 UDP 客戶端套接字
sendto(sd,*buf,len,flags,destaddr,addrlen);
sendto 函式用于 UDP 服務器端套接字與未呼叫 connect 函式的 UDP 客戶端套接字
recv(sd,*buffer,len,flags);
recv 函式從 TCP 連接的另一端接收資料,或者從呼叫了 connect 函式的 UDP 客戶端套接字接收服務器發來的資料
recvfrom(sd,*buf,len,flags,senderaddr,saddrlen);
recvfrom 函式用于從 UDP 服務器端套接字與未呼叫 connect 函式的 UDP 客戶端套接字接收對端資料
int setsockopt(intsd, intlevel, intoptname, *optval, intoptlen);
setsockopt()函式用來設定套接字 sd 的選項引數
int getsockopt(intsd, intlevel, intoptname, *optval, socklen_t*optlen);
getsockopt()函式用于獲取任意型別、任意狀態套介面的選項當前值,并把結果存入 optval
WSAStartup: 初始化 socket 庫(僅對 WinSock)
WSACleanup: 清楚/終止 socket 庫的使用(僅對 WinSock)
socket: 創建套接字 ?connect:“連接”遠端服務器(僅用于客戶端)
closesocket: 釋放/關閉套接字 ?bind: 系結套接字的本地 IP 地址和埠號(通常客戶端不需要)
listen: 置服務器端 TCP 套接字為監聽模式,并設定佇列大小(僅用于服務器端 TCP 套接字)
accept: 接受/提取一個連接請求,創建新套接字,通過新套接(僅用于服務器端的 TCP 套接字)
recv: 接收資料(用于 TCP 套接字或連接模式的客戶端 UDP 套接字)
recvfrom: 接收資料報(用于非連接模式的 UDP 套接字)
send: 發送資料(用于 TCP 套接字或連接模式的客戶端 UDP 套接字)
sendto:發送資料報(用于非連接模式的 UDP 套接字)
setsockopt: 設定套接字選項引數
getsockopt: 獲取套接字選項引數
網路位元組順序
TCP/IP 定義了標準的用于協議頭中的二進制整數表示:網路位元組順序(network byte order),某些 Socket API 函式的引數需要存盤為網路位元組順序(如 IP 地址、埠號等)
可以實作本地位元組順序與網路位元組順序間轉換的函式
htons: 本地位元組順序 → 網路位元組順序(16bits)
ntohs: 網路位元組順序 → 本地位元組順序(16bits)
htonl: 本地位元組順序 → 網路位元組順序(32bits)
ntohl: 網路位元組順序 → 本地位元組順序(32bits)
網路應用的Socket API(TCP)呼叫基本流程

Socket編程-客戶端軟體設計
決議服務器IP地址
客戶端可能使用域名(如:study.163.com)或IP地址(如:123.58.180.121)標識服務器,IP協議需要使用32位二進制IP地址,需要將域名或IP地址轉換為32位IP地址
函式inet_addr( )實作點分十進制IP地址到32位IP地址轉換
函式gethostbyname( ) 實作域名到32位IP地址轉換,回傳一個指向結構hostent的指標

決議服務器(熟知)埠號
客戶端還可能使用服務名(如HTTP)標識服務器埠,需要將服務名轉換為熟知埠號,函式getservbyname( )回傳一個指向結構servent的指標

決議協議號
客戶端可能使用協議名(如:TCP)指定協議,需要將協議名轉換為協議號(如:6),函式getprotobyname( ) 實作協議名到協議號的轉換,回傳一個指向結構protoent的指標

TCP客戶端軟體流程
1.確定服務器IP地址與埠號
2.創建套接字
3.分配本地端點地址(IP地址+埠號)
4.連接服務器(套接字)
5.遵循應用層協議進行通信
6.關閉/釋放連接
UDP客戶端軟體流程
1.確定服務器IP地址與埠號
2.創建套接字
3.分配本地端點地址(IP地址+埠號)
4.指定服務器端點地址,構造UDP資料報
5.遵循應用層協議進行通信
6.關閉/釋放套接字
Socket編程-服務器軟體設計
4種型別基本服務器
- 回圈無連接(Iterative connectionless)服務器
- 回圈面向連接(Iterative connection-oriented)服務器
- 并發無連接(Concurrent connectionless)服務器
- 并發面向連接(Concurrent connection-oriented)服務器4種型別基本服務器
回圈無連接服務器基本流程
1.創建套接字
2.系結端點地址(INADDR_ANY+埠號)
3.反復接收來自客戶端的請求
4.遵循應用層協議,構造回應報文,發送給客戶
資料發送:
服務器端不能使用connect()函式,無連接服務器使用sendto()函式發送資料報

獲取客戶端點地址:呼叫recvfrom()函式接收資料時,自動提取

回圈面向連接服務器基本流程
1.創建(主)套接字,并系結熟知埠號;
2.設定(主)套接字為被動監聽模式,準備用于服務器;
3.呼叫accept()函式接收下一個連接請求(通過主套接字),創建新套接字用于與該客戶建立連接;
4.遵循應用層協議,反復接收客戶請求,構造并發送回應(通過新套接字);
5.完成為特定客戶服務后,關閉與該客戶之間的連接,回傳步驟3.回圈面向連接服務器基本流程
并發無連接服務器基本流程
主執行緒1: 創建套接字,并系結熟知埠號;
主執行緒2: 反復呼叫recvfrom()函式,接收下一個客戶請求,并創建新執行緒處理該客戶回應;
子執行緒1: 接收一個特定請求;
子執行緒2: 依據應用層協議構造回應報文,并呼叫sendto()發送;
子執行緒3: 退出(一個子執行緒處理一個請求后即終止)
并發面向連接服務器基本流程
主執行緒1: 創建(主)套接字,并系結熟知埠號;
主執行緒2: 設定(主)套接字為被動監聽模式,準備用于服務器;
主執行緒3: 反復呼叫accept()函式接收下一個連接請求(通過主套接字),并創建一個新的子執行緒處理該客戶回應;
子執行緒1: 接收一個客戶的服務請求(通過新創建的套接字);
子執行緒2: 遵循應用層協議與特定客戶進行互動;
子執行緒3: 關閉/釋放連接并退出(執行緒終止)
例子
無連接回圈DAYTIME服務器

面向連接并發DAYTIME服務器

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/83401.html
標籤:其他
上一篇:計算機網路-p2p
下一篇:一階LC諧振電路
