1、windows sockets已經封裝好了具體的實現方法,在這裡不深究它的機制,只是能理解和熟練使用就可以。
2、tcp、udp
sock_stream
即tcp協議,要建立連線,傳送的資料,無差錯,不丟失,不重複,且按序到達。
sock_dgram即
udp協議,不建立連線,會丟包,實時傳輸。
3、網路協議:
pf_inet, af_inet: ipv4
pf_inet6, af_inet6: ipv6
af_inet 表示 ipv4 位址,例如 127.0.0.1;af_inet6 表示 ipv6 位址,例如 1030::c9b4:ff12:48aa:1a2b
127.0.0.1
,它是乙個特殊ip位址,表示本機位址
af 表示address family 位址族
pf 表示protocl family 協議族
winsock2.h中
#define af_inet 0
#define pf_inet af_inet
socket傳遞資料前需要資料封裝,裡面需要af_inet的支援。
4、實現流程:
伺服器:
建立wsadata(儲存接收的資料) -> 呼叫wsastartup函式 -> 建立socket -> 填寫ip位址和埠 -> bind()繫結ip位址和埠(判斷繫結成功) -> 監聽scoket -> 傳送和接收資料 -> 斷開連線 -> 關閉
使用dll之前必須把dll載入到當前程式,你可以在編譯時載入,也可以在程式執行時載入
方法1:
#pragma comment (lib, "ws2_32.lib")方法2:
word wversionrequested;
wversionrequested = makeword( 2, 2 ); // 請求2.2版本的winsock庫
wsastartup是任何使用winsock的應用程式或者dll首先必須呼叫winsock庫函式.一方面它初始化 ws2_32.dll,另一方面他用於在應該程式dll與系統winsock庫版本協商.
4.1、socket()函式 建立socket
intsocket(int af, int type, int protocol);af:協議域或協議簇。常用af_inet、af_inet6,決定socket的位址型別。
type:指socket型別,sock_stream、sock_dgram,即tcp、udp。
protocol:指定協議,常用的協議有,ipproto_tcp、ipptoto_udp、ipproto_sctp、ipproto_tipc等,它們分別對應tcp傳輸協議、udp傳輸協議、stcp傳輸協議、tipc傳輸協議,但是大部分設為0即可,會自動匹配。
return:返回乙個檔案的描述符,為int型,成功返回生成的socket,失敗返回invalid_socket
4.2、 bind() 繫結socket
int bind(socket sock, const struct sockaddr *addr, int addrlen);sock:是socket的檔案描述符,addr是sockaddr結構體變數的指標,addlen是addr變數的大小,可由sizeof()計算
4.3、 connect() 連線socket
int connect(socket sock, const struct sockaddr *serv_addr, int addrlen);引數跟bind()一致
4.4、listen() 監聽socket
int
listen
(socket
sock,
int backlog);
sock 為需要進入監聽狀態的套接字,backlog 為請求佇列的最大長度
緩衝區的長度(能存放多少個客戶端請求)可以通過 listen() 函式的 backlog 引數指定,但究竟為多少並沒有什麼標準,可以根據你的需求來定,併發量小的話可以是10或者20。
如果將 backlog 的值設定為 somaxconn,就由系統來決定請求佇列長度,這個值一般比較大,可能是幾百,或者更多。
當請求佇列滿時,就不再接收新的請求,對於 linux,客戶端會收到 econnrefused 錯誤,對於 windows,客戶端會收到 wsaeconnrefused 錯誤。
注意:listen() 只是讓套接字處於監聽狀態,並沒有接收請求。接收請求需要使用 accept() 函式。
4.4、 accept() 接收socket
socket
accept
(socket
sock,
struct
sockaddr
*addr,
int*addrlen);
它的引數與 listen() 和 connect() 是相同的:sock 為伺服器端套接字,addr 為 sockaddr_in 結構體變數,addrlen 為引數 addr 的長度,可由 sizeof() 求得。
accept() 返回乙個新的套接字來和客戶端通訊,addr 儲存了客戶端的ip位址和埠號,而 sock 是伺服器端的套接字,大家注意區分。後面和客戶端通訊時,要使用這個新生成的套接字,而不是原來伺服器端的套接字。
最後需要說明的是:listen() 只是讓套接字進入監聽狀態,並沒有真正接收客戶端請求,listen() 後面的**會繼續執行,直到遇到 accept()。accept() 會阻塞程式執行(後面**不能被執行),直到有新的請求到來。
4.5、send()和recv()
從伺服器端傳送資料使用 send() 函式,它的原型為:
int send(socket sock, const char *buf, int len, int flags);sock 為要傳送資料的套接字,buf 為要傳送的資料的緩衝區位址,len 為要傳送的資料的位元組數,flags 為傳送資料時的選項。
返回值和前三個引數不再贅述,最後的 flags 引數一般設定為 0 或 null,初學者不必深究。
在客戶端接收資料使用 recv() 函式,它的原型為:
int recv(socket sock, char *buf, int len, int flags);4.6、返回值
socket() accept()
如果成功就返回生成的socket,如果失敗就返回invalid_socket.
#define invalid_socket (socket)(~0)
實際上是 0xffffffff 4bytes
bind() listen() connect()
如果成功就返回0,如果失敗就返回socket_error,需要通過wsagetlasterror獲得進一步的錯誤資訊.
#define socket_error (-1)
實際上是 0xffffffff 4bytes
send() sendto()
如果成功就返回傳送的位元組數,如果失敗就返回socket_error,需要通過wsagetlasterror獲得進一步的錯誤資訊.
recv() recvfrom()
如果成功就返**到的位元組數,如果如果失敗就返回socket_error,需要通過wsagetlasterror獲得進一步的錯誤資訊.
如果連線被溫和的關閉,返回0,但是recvfrom通常是用於無連線的udp socket.
靜態聯編與動態聯編
在c 中,多型性主要是通過函式過載實現的。過載函式是指程式中對同名函式進行呼叫時,編譯器會根據函式引數的型別和個數,決定該呼叫哪一段函式 來處理這個函式呼叫。這種把函式呼叫與適當的函式 相對應的動作,叫做聯編。聯編分為靜態聯編和動態聯編。在編譯階段決定執行哪個同名的被呼叫函式,稱為靜態聯編。在編譯階...
靜態聯編和動態聯編
聯編是指乙個電腦程式自身彼此關聯 使乙個 源程式經過編譯 連線,成為乙個可執行程式 的過程,在這個聯編過程中,需要確定程式中的操作呼叫 函式呼叫 與執行該操作 函式 的 段之間的對映關係,按照聯編所進行的階段不同,可分為靜態聯編和動態聯編。靜態聯編 呼叫函式和被調函式在程式編譯時,他們在記憶體中的位...
靜態聯編和動態聯編
聯編就是將模組或者函式合併在一起生成可執行 的處理過程,同時對每個模組或者函式呼叫分配記憶體位址,並且對外部訪問也分配正確的記憶體位址,它是電腦程式彼此關聯的過程。按照聯編所進行的階段不同,可分為兩種不同的聯編方法 靜態聯編和動態聯編。靜態聯編是指在編譯階段就將函式實現和函式呼叫關聯起來,因此靜態聯...