1.Socket頭文件的包含及庫的鏈接:
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
2.初始化和卸載
- WSAStartup
int WSAStartup(
__in WORD wVersionRequested,
__out LPWSADATA lpWSAData
);
wVersionRequested
調用程序使用windows socket的最高版本。 高字節指定小的版本號,低字節指定高的版本號。
lpWSAData
指向WSADATA數據結構體指針,接收Windows Socket的實現細節。
返回值
如果成功,WSAStartup函數返回0。否則,返回下面列表顯示的錯誤碼之一。
WSAStartup函數直接在返回值中返回擴展的錯誤碼,不再需要調用WSAGetLastError函數。
錯誤碼 | 解釋 |
WSASYSNOTREADY | 網絡通信中下層的網絡子系統沒準備好 |
WSAVERNOTSUPPORTED | Socket實現提供版本和socket需要的版本不符 |
WSAEINPROGRESS | 一個阻塞的Socket操作正在進行 |
WSAEPROCLIM | Socket的實現超過Socket支持的任務數限制 |
WSAEFAULT | lpWSAData參數不是一個合法的指針 |
- MAKEWORD
WORD MAKEWORD(
BYTE bLow, //指定新變量的低字節序;
BYTE bHigh //指定新變量的高字節序;
);
MAKEWORD 宏 平臺:SDK這個宏創建一個無符號16位整型,通過連接兩個給定的無符號參數。(注:typedef unsigned short WORD;)
宏的原始定義:
#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
返回值:一個無符號16位整形數。
- 初始化:
BOOL WinSockInit()
{
WSADATA wsaData = {0};
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR) {
return TRUE;
}else{
return FALSE;
}
}
- 卸載:
void WinSockUnLoad()
{
WSACleanup();
}
3.Socket函數:
- socket
SOCKET WSAAPI socket(
_In_ int af, //地址描述
_In_ int type, //套接字類型
_In_ int protocol //協議類型
);
domain:協議族,
常用的有AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE其中AF_INET代表使用ipv4地址
AF_INET,指定so_pcb中的地址要採用ipv4地址類型
AF_INET6,指定so_pcb中的地址要採用ipv6的地址類型
AF_LOCAL/AF_UNIX,指定so_pcb中的地址要使用絕對路徑名
type:
socket類型,常用的socket類型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
SOCK_STREAM是基於TCP的,數據傳輸比較有保障。SOCK_DGRAM是基於UDP的,專門用於局域網,基於廣播SOCK_STREAM 是數據流,一般是tcp/ip協議的編程,SOCK_DGRAM分是數據抱,是udp協議網絡編程
protocol:協議。
常用的協議有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等
IPPROTO_TCP,TCP協議
IPPROTO_UDP,UPD協議
0,如果指定爲0,表示由內核根據so_type指定默認的通信協議
成功時,返回非負數的socket描述符;失敗是返回-1。socket描述符是一個指向內部數據結構的指針
使用:
//創建套接字
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET)
{
cout << "serverSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
return -1;
}
- bind
int bind(
_In_ SOCKET s, //套接字句柄
_In_ const struct sockaddr *name, //要關聯的本地地址
_In_ int namelen //地址的長度
);
使用:
//初始化服務器地址族變量
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000); //端口號
//綁定
iRet = bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
if (iRet == SOCKET_ERROR)
{
cout << "bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) execute failed!" << endl;
return -1;
}
- listen
int listen(
_In_ SOCKET s, //套接字句柄
_In_ int backlog //監聽隊列中允許保持的尚未處理的最大連接數量
);
使用:
//監聽
iRet = listen(serverSocket, CONNECT_NUM_MAX);
if (iRet == SOCKET_ERROR)
{
cout << "listen(serverSocket, 10) execute failed!" << endl;
return -1;
}
- accept
SOCKET accept(
_In_ SOCKET s, //套接字句柄
_Out_ struct sockaddr *addr, //一個指向 sockaddr_in 結構的指針,用於取得對方的地址信息
_Inout_ int *addrlen //一個指向地址長度的指針
);
使用:
//等待連接_接收_發送
SOCKADDR_IN clientAddr;
int len = sizeof(SOCKADDR);
SOCKET connSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &len);
if (connSocket == INVALID_SOCKET)
{
cout << "accept(serverSocket, (SOCKADDR*)&clientAddr, &len) execute failed!" << endl;
return -1;
}
4.簡單的例子
- 服務器端代碼:
#include<iostream>
#include<WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#define CONNECT_NUM_MAX 10
using namespace std;
int main()
{
//加載套接字庫
WSADATA wsaData;
int iRet = 0;
iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iRet != 0)
{
cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;;
return -1;
}
if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
{
WSACleanup();
cout << "WSADATA version is not correct!" << endl;
return -1;
}
//創建套接字
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET)
{
cout << "serverSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
return -1;
}
//初始化服務器地址族變量
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
//綁定
iRet = bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
if (iRet == SOCKET_ERROR)
{
cout << "bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) execute failed!" << endl;
return -1;
}
//監聽
iRet = listen(serverSocket, CONNECT_NUM_MAX);
if (iRet == SOCKET_ERROR)
{
cout << "listen(serverSocket, 10) execute failed!" << endl;
return -1;
}
//等待連接_接收_發送
SOCKADDR_IN clientAddr;
int len = sizeof(SOCKADDR);
while (1)
{
SOCKET connSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &len);
if (connSocket == INVALID_SOCKET)
{
cout << "accept(serverSocket, (SOCKADDR*)&clientAddr, &len) execute failed!" << endl;
return -1;
}
char sendBuf[100];
sprintf_s(sendBuf, "Welcome %s", inet_ntoa(clientAddr.sin_addr));
send(connSocket, sendBuf, strlen(sendBuf)+1, 0);
char recvBuf[100];
recv(connSocket, recvBuf, 100, 0);
printf("%s\n", recvBuf);
closesocket(connSocket);
}
system("pause");
return 0;
}
- 客戶端代碼:
#include<iostream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
//加載套接字庫
WSADATA wsaData;
int iRet =0;
iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iRet != 0)
{
cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;
return -1;
}
if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
{
WSACleanup();
cout << "WSADATA version is not correct!" << endl;
return -1;
}
//創建套接字
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
{
cout << "clientSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
return -1;
}
//初始化服務器端地址族變量
SOCKADDR_IN srvAddr;
srvAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(6000);
//連接服務器
iRet = connect(clientSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));
if (0 != iRet)
{
cout << "connect(clientSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR)) execute failed!" << endl;
return -1;
}
//接收消息
char recvBuf[100];
recv(clientSocket, recvBuf, 100, 0);
printf("%s\n", recvBuf);
//發送消息
char sendBuf[100];
sprintf_s(sendBuf, "Hello, This is client %s", "兔子");
send(clientSocket, sendBuf, strlen(sendBuf)+1, 0);
//清理
closesocket(clientSocket);
WSACleanup();
system("pause");
return 0;
}
- 程序運行截圖: