[C++轉C#]Socket部分- C++總覽
目的:如題,Socket通訊部分要從C++轉換到C#
方法:確認C++和C#在socket通訊上的差異
以下蒐集各方大大們(鞠躬)的資料:
一、關於C++的socket通訊
[轉自XYZ的筆記本]
Server 端執行流程:
link Winsock Library(windows環境下) -> 初始化 Windows Sockets DLL(windows環境下) -> 使用 socket() 建立 socket descriptor -> 設定 Server 位址資訊 -> 設定 listen() -> 等待連線 -> 接受連線 accect() -> 傳送接收資料
Client 端執行流程:
link Winsock Library(windows環境下) -> 初始化 Windows Sockets DLL(windows環境下) -> 使用 socket() 建立 socket descriptor -> 設定 Server 位址資訊 -> 連線到 Server 端 connect() -> 傳送接收資料
測試環境 Visual Studio Express 2012
在 Windows 使用 Socket 需要 link Winsock Library。[server端] [client端]
link方式:
專案的「屬性」->「組態屬性」->「連結器」->「輸入」->「其他相依性」加入 wsock32.lib 或 Ws2_32.lib
也可以在程式中,使用以下方式加入
#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。
初始化 Windows Sockets DLL。[server端] [client端]
範例:
綁定 socket 的位址資料 (bind)。[server端]
函式: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
範例:
期限:12月底
方法:確認C++和C#在socket通訊上的差異
以下蒐集各方大大們(鞠躬)的資料:
一、關於C++的socket通訊
[轉自XYZ的筆記本]
Server 端執行流程:
link Winsock Library(windows環境下) -> 初始化 Windows Sockets DLL(windows環境下) -> 使用 socket() 建立 socket descriptor -> 設定 Server 位址資訊 -> 設定 listen() -> 等待連線 -> 接受連線 accect() -> 傳送接收資料
Client 端執行流程:
link Winsock Library(windows環境下) -> 初始化 Windows Sockets DLL(windows環境下) -> 使用 socket() 建立 socket descriptor -> 設定 Server 位址資訊 -> 連線到 Server 端 connect() -> 傳送接收資料
測試環境 Visual Studio Express 2012
在 Windows 使用 Socket 需要 link Winsock Library。[server端] [client端]
link方式:
專案的「屬性」->「組態屬性」->「連結器」->「輸入」->「其他相依性」加入 wsock32.lib 或 Ws2_32.lib
也可以在程式中,使用以下方式加入
#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。
初始化 Windows Sockets DLL。[server端] [client端]
範例:
WSAData wsaData;
WORD
version = MAKEWORD(2, 2);
// 版本
int
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
// 成功回傳 0
if
(iResult != 0) {
// 初始化Winsock 失敗
}
*建立 socket 描述符 (socket descriptor)。[server端] [client端]
函式:SOCKET socket( int af, int type, int protocol);
int af:使用何種通訊家族。
例如
AF_INET:使用 IPv4
AF_INET6:使用 IPv6
int type:The type specification for the new socket.
能用的值跟第 1 個參數有關
例如
SOCK_STREAM:使用 TCP 協議
SOCK_DGRAM:使用 UDP 協議
int protocol:The protocol to be used.
能用的值跟前面兩個參數有關
例如
IPPROTO_TCP:使用 TCP
IPPROTO_UDP:使用 UDP
成功回傳 socket descriptor,失敗回傳 INVALID_SOCKET
範例:
SOCKET sListen = INVALID_SOCKET;
sListen= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if
(sListen== INVALID_SOCKET) {
// 建立失敗
}
設定位址資訊的資料 (SOCKADDR_IN)。[server端] [client端]
*結構:
使用 IP4 格式結構 struct sockaddr_in (in 表示 internet) 設定 internet 位址資訊。
*範例:
SOCKADDR_IN addr;
memset
(&addr, 0,
sizeof
(addr)) ;
// 清空,將資料設為 0
addr.sin_addr.s_addr = inet_addr(
"127.0.0.1"
);
// 設定 IP,後面解釋 inet_addr()
// addr.sin_addr.s_addr = INADDR_ANY; // 若設定 INADDR_ANY 表示任何 IP
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
// 設定 port,後面解釋 htons()
struct sockaddr_in:IP4 格式使用
struct sockaddr_in6:IP6 格式使用
struct sockaddr:通用格式
struct sockaddr_un:UNIX domain 格式
htons()
使用 htons() 是因為網路位元順序 (Network Byte Order, 縮寫NBO) 可能跟電腦主機位元順序(Host Byte Order, 縮寫HBO)不同,所以需要要函式來轉換,以增加移植性。
htons():"h(host)" "to" "n(network)" "s(short)",Host to Network Short,將 short (2byte) 資料順序從 host 轉換至 network。
htonl():Host to Network Long,將 long(4byte) 資料順序從 host 轉換至 network。
ntohs():Network to Host Short,將 short (2byte) 資料順序從 network 轉換至 host。
ntohl():Network to Host Long,將 long(4byte) 資料順序從 network 轉換至 host。
inet_addr()
將 IP 轉換為 unsigned long 格式,轉換後,已是網路位元順序 (Network Byte Order,所以不用再使用 htons()
綁定 socket 的位址資料 (bind)。[server端]
函式: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
範例:
// addr 為 sockaddr_in 結構,強制轉型為 sockaddr 結構
int
r = bind(sListen, (SOCKADDR*)&addr,
sizeof
(addr));
assert
(r != SOCKET_ERROR);
監聽連線 (listen)。[server端]
函式:int listen(SOCKET s, int backlog)
SOCKET s:socket() 函式回傳的 socket descriptor。
int backlog:最大可監聽多少連線(佇列、排隊)。設定 SOMAXCONN 表示系統最大值。
成功回傳 0,失敗回傳 SOCKET_ERROR。
connection-oriented ( SOCK_STREAM ) 的 server 程式端程式才使用。
範例:
// addr 為 sockaddr_in 結構,強制轉型為 sockaddr 結構
int
r = listen(sListen, SOMAXCONN);
assert
(r != SOCKET_ERROR);
處理連線 (accept)。[server端]
函式:SOCKET accept(SOCKET s, struct sockaddr *addr, int *addrlen)
SOCKET s:socket() 函式回傳的 socket descriptor。
struct sockaddr *addr:結果參數,預先配置 SOCKADDR 結構的指標,用來存放客戶端位址,不存放可設置為 NULL。
int *addrlen:結果參數,第二個參數的大小,不存放也可以設為 NULL。
成功回傳 socket descriptor,失敗回傳 INVALID_SOCKET,可用 WSAGetLastError() 取得 error code。
所以成功時,會產生一個新的 socket descriptor,而原本的 socket descriptor 持續監聽原來的端口。
connection-oriented ( SOCK_STREAM ) 的 server 程式端程式才使用。
範例:
SOCKET sConnect;
struct
sockaddr_in clientAddr;
// client 端位址資訊
int
clientAddrLen =
sizeof
(clientAddr);
sConnect = accept(sListen, (SOCKADDR*)&clientAddr, &clientAddrLen);
// sConnect = accept(sListen, NULL, NULL);
if
(sConnect != INVALID_SOCKET)
{
// 有 client 端成功連線過來
printf
(
"server: got connection from %s"
, inet_ntoa(clientAddr.sin_addr));
}
連線到 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。
範例:
int
r = connect(sConnect, (SOCKADDR*)&addr,
sizeof
(addr));
if
(r != SOCKET_ERROR){
// 連線成功
}
傳送訊息。[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)
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,可用 WSAGetLastError() 取得 error code。
範例:
1
2
char
*sendbuf =
"sending data test"
;
send(sConnect, sendbuf, (
int
)
strlen
(sendbuf), 0);
接收訊息。[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)
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,可用 WSAGetLastError() 取得 error code。
範例:
1
2
3
char
message[200];
ZeroMemory(message, 200);
recv(sConnect, message,
sizeof
(message), 0);
- 設定 socket option 選項。[server端] [client端]
函式 :int setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen)
用來設定建立的 socket 一些特性,例如是否強制關閉等。
使用範例可參考:setsockopt 设置socket - 驱动开发 - 博客频道 - CSDN.NET
關閉 socket。[server端] [client端]
函式 :int closesocket(SOCKET s)
Scoket Server 範例:
執行後會等待 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;
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;
while
(
true
)
{
cout <<
"waiting..."
<< endl;
if
(sConnect = accept(sListen, (SOCKADDR*)&clinetAddr, &addrlen))
{
cout <<
"a connection was found"
<< endl;
printf
(
"server: got connection from %s\n"
, inet_ntoa(addr.sin_addr));
//傳送訊息給 client 端
char
*sendbuf =
"sending data test"
;
send(sConnect, sendbuf, (
int
)
strlen
(sendbuf), 0);
}
}
//getchar();
}
Socket Client 範例:
執行後,按 "Y" 連線到 Server 端,並接收 Server 端傳過來的訊息。
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
#include <string>
using
namespace
std;
void
main()
{
string confirm;
char
message[200];
//開始 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);
cout <<
"connect to server?[Y] or [N]"
<< endl;
cin >> confirm;
if
(confirm ==
"N"
)
{
exit
(1);
}
else
{
if
(confirm ==
"Y"
)
{
connect(sConnect, (SOCKADDR*)&addr,
sizeof
(addr));
//接收 server 端的訊息
ZeroMemory(message, 200);
r = recv(sConnect, message,
sizeof
(message), 0);
cout << message << endl;
//設定 closesocket 時,不經過 TIME-WAIT 過程,直接關閉socket
//BOOL bDontLinger = FALSE;
//setsockopt(sConnect,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
//若之後不再使用,可用 closesocket 關閉連線
closesocket(sConnect);
getchar
();
getchar
();
}
}
}
其他:
執行範例時,可另開一個指令視窗,執行以下指令,觀察每1秒鐘 1234 port 的監聽、連線狀態netstat -na -p TCP 1 | find ":1234"
參考:
WinSock.h & WinSock2.h which to use?
Socket中winsock.h和winsock2.h的不同
http://en.wikipedia.org/wiki/Winsock
Hook MSN Messenger之socket通訊的鳥事
MSDN WSAStartup function
MSDN socket function
【網络編程基礎筆記】struct sockaddr和struct sockaddr_in的區別和用法
整理:Linux網络編程之sockaddr與sockaddr_in,sockaddr_un結構體詳細講解
SOCKADDR 結構
htonl() htons()及inet_ntoa() inet_addr()的用法
【轉】struct sockaddr與struct sockaddr_in的區別和聯繫
MSDN sockaddr_in
MSDN inet_addr function
MSDN bind function
MSDN listen function
MSDN accept function
網絡編程socket之accept函數
MSDN connect function
MSDN send function
MSDN sendto function
MSDN recv function
MSDN recvfrom function
MSDN setsockopt function
MSDN closesocket function
Beej網絡socket編程指南
SOCKET筆記~
Socket 程式設計
MSDN Complete Winsock Server Code
MSDN Complete Winsock Client Code
C++ Server & Client Tutorial pt.1
C++ Server & Client Tutorial pt.2
C++ Chat Application
輕描淡寫的低調: TCP TIME_WAIT的釋義
傳輸控制協定 - 維基百科,自由的百科全書
(感謝大大!!)
留言
張貼留言