1.在 Windows 使用 Socket 需要 link Winsock Library。[server端] [client端]
在程式中加入
#pragma comment(lib, "wsock32.lib") 或 #pragma comment(lib, "Ws2_32.lib")
wsock32.lib 和 Ws2_32.lib 的區別:
- wsock32.lib 是較舊的 1.1 版本,Ws2_32.lib 是較新的 2.0 版本。
- wsock32.lib 跟 winsock.h 一起使用,Ws2_32.lib 跟 WinSock2.h 一起使用。
- winsock.h 和 WinSock2.h 不能同時使用,WinSock2.h 是用來取代 winsock.h,而不是擴展 winsock.h。
2.建立 socket descriptor。[server端] [client端]
函式:SOCKET socket( int af, int type, int protocol);
int af:使用何種通訊。
函式:SOCKET socket( int af, int type, int protocol);
int af:使用何種通訊。
- 例如
AF_INET:使用 IPv4
AF_INET6:使用 IPv6
- 能用的值跟 " int af " 參數有關
- 例如
SOCK_STREAM:使用 TCP 協議
SOCK_DGRAM:使用 UDP 協議
- 例如
IPPROTO_TCP:使用 TCP
IPPROTO_UDP:使用 UDP
3.設定位址資訊的資料 (SOCKADDR_IN)。[server端] [client端]
結構:使用 IP4 格式結構 struct sockaddr_in (in 表示 internet) 設定 internet 位址資訊。
結構:使用 IP4 格式結構 struct sockaddr_in (in 表示 internet) 設定 internet 位址資訊。
- struct sockaddr_in:IPv4 使用
- struct sockaddr_in6:IPv6 使用
- struct sockaddr:通用格式
- struct sockaddr_un:UNIX domain 格式
函式:int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
int sockfd:socket() 函式回傳的 socket descriptor
struct sockaddr *my_addr:用來通訊的位址資料(IP、PORT)
int addrlen:位址長度 ,sizeof(my_addr)
綁定成功回傳 0,失敗回傳 SOCKET_ERROR。可善用 " SOCKET_ERROR " 來檢查是否成功。
5.監聽連線 (listen)。[server端] 函式:int listen(SOCKET s, int backlog)
- SOCKET s:socket() 函式回傳的 socket descriptor。
- int backlog:最大可監聽多少連線(佇列、排隊)。設定 SOMAXCONN 表示系統最大值。
- 成功回傳 0,失敗回傳 SOCKET_ERROR。
- connection-oriented ( SOCK_STREAM ) 的 server 程式端程式才使用。
6.連線到 socket Server。[client端]
函式:int connect(SOCKET s, const struct sockaddr *name, int namelen)
- SOCKET s:socket() 函式回傳的 socket descriptor。
- const struct sockaddr *name:Server 端的位址資料。
- int namelen:第二個參數的大小。
- 成功回傳 0,失敗回傳 SOCKET_ERROR,可用 WSAGetLastError() 取得 error code。
7.傳送訊息。[server端] [client端]
函式 1:int send(SOCKET s, const char *buf, int len, int flags)
函式 2:int sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen)
函式 1:int send(SOCKET s, const char *buf, int len, int flags)
函式 2:int sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen)
- send() 和 sendto() 的差異在於,send() 只能用在 connection-oriented ( SOCK_STREAM ) 的連線,所以 sendto() 比 send() 多最後兩個參數,用來指定目的地的位址資訊。
- SOCKET s:socket() 函式回傳的 socket descriptor。
- const char *buf:訊息的指標。
- int len:訊息的長度。
- int flags:The flags parameter can be used to influence the behavior of the function beyond the options specified for the associated socket。(一般設 0)
MSG_DONTROUTE:不將訊息送給 gateway,而直接送給 host。(Specifies that the data should not be subject to routing. A Windows Sockets service provider can choose to ignore this flag.)
MSG_OOB:Sends OOB data (stream-style socket such as SOCK_STREAM only)。
- const struct sockaddr *to:目的地位址資訊。
- int tolen:目的地位址資訊的大小。
- 成功回傳傳送的資料長度,失敗回傳 SOCKET_ERROR。
8.接收訊息。[server端] [client端]
函式 1:int recv(SOCKET s, char *buf, int len, int flags)
函式 2:int recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
函式 1:int recv(SOCKET s, char *buf, int len, int flags)
函式 2:int recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
- recv() 和 recvfrom() 的差異在於,recv() 只能用在 connection-oriented ( SOCK_STREAM ) 的連線,所以 recvfrom() 比 recv() 多最後兩個參數,用來指定接收來源的位址資訊。
- SOCKET s:socket() 函式回傳的 socket descriptor。
- const char *buf:訊息的指標。
- int len:訊息的長度。
- int flags:The flags parameter can be used to influence the behavior of the function invocation beyond the options specified for the associated socket.。(一般設 0)
MSG_PEEK:Peeks at the incoming data。只看訊息內容,但不將訊息從 queue 移除。
MSG_OOB:Processes Out Of Band (OOB) data。
- const struct sockaddr *to:目的地位址資訊。
- int tolen:目的地位址資訊的大小。
- 成功回傳接收的資料長度,連線被關閉回傳 0。失敗回傳 SOCKET_ERROR。
9.設定 socket option 選項。[server端] [client端]
函式 :int setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen)
用來設定建立的 socket 一些特性,例如是否強制關閉等。
10.關閉 socket。[server端] [client端]
函式 :int closesocket(SOCKET s)
函式 :int setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen)
用來設定建立的 socket 一些特性,例如是否強制關閉等。
10.關閉 socket。[server端] [client端]
函式 :int closesocket(SOCKET s)
===============================================================
編譯環境
win7 + CodeBlocks16.01 + MinGW
編譯環境
win7 + CodeBlocks16.01 + MinGW
Scoket Server 範例:
執行後會等待 client 端連線,有連線進來時,則傳送 "sending data test" 訊息給 client 端。
執行後會等待 client 端連線,有連線進來時,則傳送 "sending data test" 訊息給 client 端。
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
using
namespace
std;
void
main()
{
int
r;
WSAData wsaData;
WORD
DLLVSERION;
char *message;
DLLVSERION = MAKEWORD(2,1);
//Winsocket-DLL 版本
//用 WSAStartup 開始 Winsocket-DLL
r = WSAStartup(DLLVSERION, &wsaData);
//宣告 socket 位址資訊
SOCKADDR_IN addr;
int
addrlen =
sizeof
(addr);
//建立 socket
SOCKET sListen;
//listening for an incoming connection
SOCKET sConnect;
//operating if a connection was found
//AF_INET:表示建立的 socket 屬於 internet family
//SOCK_STREAM:表示建立的 socket 是 connection-oriented socket
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
//設定位址資訊的資料
addr.sin_addr.s_addr = inet_addr(
"127.0.0.1"
);
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
//設定 Listen
sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&addr,
sizeof
(addr));
listen(sListen, SOMAXCONN);
//SOMAXCONN: listening without any limit
//等待連線
SOCKADDR_IN clinetAddr;
c = sizeof(struct sockaddr_in);
while( (sConnect = accept(sListen , (struct sockaddr *)&clinetAddr, &c)) != INVALID_SOCKET ){
puts("Connection accepted");
//Reply to the client
message = "Hello Client , I have received your connection. But I have to go now, bye\n";
send(sConnect , message , strlen(message) , 0);
}
}
Socket Client 範例:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
#include <string>
using
namespace
std;
void
main()
{
string confirm;
char
server_reply[2000];
//開始 Winsock-DLL
int
r;
WSAData wsaData;
WORD
DLLVersion;
DLLVersion = MAKEWORD(2,1);
r = WSAStartup(DLLVersion, &wsaData);
//宣告給 socket 使用的 sockadder_in 結構
SOCKADDR_IN addr;
int
addlen =
sizeof
(addr);
//設定 socket
SOCKET sConnect;
//AF_INET: internet-family
//SOCKET_STREAM: connection-oriented socket
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
//設定 addr 資料
addr.sin_addr.s_addr = inet_addr(
"127.0.0.1"
);
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
connect(sConnect, (SOCKADDR*)&addr,
sizeof
(addr));
//接收 server 端的訊息
puts("Connected");
//Receive a reply from the server
if((recv_size = recv(sConnect , server_reply , 2000 , 0)) == SOCKET_ERROR){
puts("recv failed");
}
puts("Reply received\n");
//Add a NULL terminating character to make it a proper string before printing
server_reply[recv_size] = '\0';
puts(server_reply);
//設定 closesocket 時,不經過 TIME-WAIT 過程,直接關閉socket
//BOOL bDontLinger = FALSE;
//setsockopt(sConnect,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
//若之後不再使用,可用 closesocket 關閉連線
closesocket(sConnect);