Windows編程(網絡編程)
套接字類型與協議設置
SOCK_STREAM
[流套接字] TCP
面向連接、可靠的數據傳輸 適合傳輸大量的數據,不支持廣播、多播
SOCK_DGRAM
[數據包套接字] UDP
無連接 支持廣播、多播
SOCK_RAW
[原始套接字]
可以讀寫內核沒有處理的 IP 數據報
避開 TCP/IP 處理機制,被傳送的數據報可以被直接傳送給需要它的的應用程序
-引用頭文件 winsock2.h
- 導入 ws2_32.lib 庫
- window 下 socket 編程都要首先進行 Winsock 的初始化
使用:
SOCKET WSAAPI socket(
int af,
int type,
int protocol
);
af參數:
當前支持的值為 AF_INET 或 AF_INET6,它們是 IPv4 和 IPv6 的 Internet 地址族格式。如果安裝了地址族的 Windows 套接字服務提供程序,則支持地址族的其他選項(例如,用於 NetBIOS 的 AF_NETBIOS)。請注意,AF_ 地址族和 PF_ 協議族常量的值是相同的(例如,AF_INET和PF_INET),因此可以使用任一常量。
AF_UNSPEC0 | 地址族未指定。 |
---|---|
**AF_INET ** | Internet 協議版本 4 (IPv4) 地址族。 |
AF_IPX | IPX/SPX 地址族。僅當安裝了 NWLink IPX/SPX NetBIOS 兼容傳輸協議時才支持此地址系列。Windows Vista 及更高版本不支持此地址系列。 |
AF_APPLETALK | AppleTalk 地址族。僅當安裝了 AppleTalk 協議時才支持此地址族。Windows Vista 及更高版本不支持此地址系列。 |
AF_NETBIOS | NetBIOS 地址族。僅當安裝了 NetBIOS 的 Windows 套接字提供程序時才支持此地址族。32 位版本的 Windows 支持 NetBIOS 的 Windows 套接字提供程序。默認情況下,此提供程序安裝在 32 位版本的 Windows 上。64 位版本的 Windows 不支持 NetBIOS 的 Windows 套接字提供程序,包括 Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 或 Windows XP。NetBIOS 的 Windows 套接字提供程序僅支持類型參數設置為SOCK_DGRAM 的套接字。NetBIOS 的 Windows 套接字提供程序與NetBIOS編程接口沒有直接關系。Windows Vista、Windows Server 2008 及更高版本不支持 NetBIOS 編程接口。 |
AF_INET6 | Internet 協議版本 6 (IPv6) 地址族。 |
AF_IRDA | 紅外數據協會 (IrDA) 地址族。僅當計算機安裝了紅外端口和驅動程序時,才支持此地址族。 |
AF_BTH | 藍牙地址族。如果計算機安裝了藍牙適配器和驅動程序,則 Windows XP SP2 或更高版本支持此地址系列。 |
type參數:
新套接字的類型規范。
套接字類型的可能值在Winsock2.h頭文件中定義。
在 Windows Sockets 1.1 中,唯一可能的套接字類型是SOCK_DGRAM和SOCK_STREAM。
類型 | 意義 |
---|---|
SOCK_STREAM | 一種套接字類型,通過 OOB 數據傳輸機制提供有序的、可靠的、雙向的、基於連接的字節流。此套接字類型使用 Internet 地址族(AF_INET 或 AF_INET6)的傳輸控制協議 (TCP)。 |
SOCK_DGRAM | 支持數據報的套接字類型,數據報是固定(通常很小)最大長度的無連接、不可靠的緩沖區。此套接字類型使用 Internet 地址族(AF_INET 或 AF_INET6)的用戶數據報協議 (UDP)。 |
SOCK_RAW | 提供原始套接字的套接字類型,允許應用程序操作下一個上層協議標頭。要操作 IPv4 標頭,必須在套接字上設置IP_HDRINCL套接字選項。要操作 IPv6 標頭,必須在套接字上設置IPV6_HDRINCL套接字選項。 |
SOCK_RDM | 提供可靠消息數據報的套接字類型。這種類型的一個例子是 Windows 中的實用通用多播 (PGM) 多播協議實現,通常稱為可靠多播編程。僅當安裝了可靠多播協議時才支持此類型值。 |
SOCK_SEQPACKET | 一種基於數據報提供偽流數據包的套接字類型。 |
protocol參數
要使用的協議。協議參數的可能選項特定於指定的地址族和套接字類型。協議的可能值在Winsock2.h和Wsrm.h頭文件中定義 。
在為 Windows Vista 及更高版本發布的 Windows SDK 上,頭文件的組織已更改,此參數可以是Ws2def.h頭文件中定義的IPPROTO枚舉類型的值之一。請注意,Ws2def.h頭文件自動包含在Winsock2.h 中,不應直接使用。
如果指定值為 0,則調用者不希望指定協議,服務提供者將選擇要使用的協議。
當af參數為 AF_INET 或 AF_INET6 且類型為SOCK_RAW 時,在 IPv6 或 IPv4 數據包頭的協議字段中設置為協議指定的值。
協議 | 意義 |
---|---|
IPPROTO_ICMP | Internet 控制消息協議 (ICMP)。當af參數為AF_UNSPEC、AF_INET或AF_INET6並且類型參數為SOCK_RAW或未指定時,這是一個可能的值。Windows XP 及更高版本支持此協議值。 |
IPPROTO_IGMP | Internet 組管理協議 (IGMP)。當af參數為AF_UNSPEC、AF_INET或AF_INET6並且類型參數為SOCK_RAW或未指定時,這是一個可能的值。Windows XP 及更高版本支持此協議值。 |
BTHPROTO_RFCOMM | 藍牙射頻通信 (Bluetooth RFCOMM) 協議。當af參數為AF_BTH且類型參數為SOCK_STREAM時,這是一個可能的值。帶有 SP2 或更高版本的 Windows XP 支持此協議值。 |
IPPROTO_TCP | 傳輸控制協議 (TCP)。當af參數為AF_INET或AF_INET6並且類型參數為SOCK_STREAM時,這是一個可能的值。 |
IPPROTO_UDP | 用戶數據報協議 (UDP)。當af參數是AF_INET或AF_INET6並且類型參數是SOCK_DGRAM時,這是一個可能的值。 |
IPPROTO_ICMPV6 | Internet 控制消息協議版本 6 (ICMPv6)。當af參數為AF_UNSPEC、AF_INET或AF_INET6 並且類型參數為SOCK_RAW或未指定時,這是一個可能的值。Windows XP 及更高版本支持此協議值。 |
IPPROTO_RM | 用於可靠組播的 PGM 協議。當af參數是AF_INET並且類型參數是SOCK_RDM時,這是一個可能的值。在為 Windows Vista 及更高版本發布的 Windows SDK 上,此協議也稱為IPPROTO_PGM。僅當安裝了可靠多播協議時才支持此協議值。 |
TCP 通訊
SOCKADDR_IN
原型:
typedef struct sockaddr_in {
USHORT sin_port;//16 位地址類型
IN_ADDR sin_addr;//16 位端口號 65535 2 的 16 次方
CHAR sin_zero[8];//8 字節填充
}
IN_ADDR:
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
Server端
代碼實例:
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include<stdio.h>
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main() {
//1 初始化網絡庫
// 加載套接字庫
WORD wVersionRequested; WSADATA wsaData;
int err; wVersionRequested = MAKEWORD(2, 2);
// 1、初始化套接字庫
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) { printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockSrv) {
printf("socket errorNum = %d\n", GetLastError());
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//任意地址連接
addrSrv.sin_family = AF_INET; //設置ipv4
addrSrv.sin_port = htons(6001); //設置端口
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))) {
printf("bind errorNum = %d\n", GetLastError()); return -1;
}
if (SOCKET_ERROR == listen(sockSrv, 5)) {
printf("listen errorNum = %d\n", GetLastError()); return -1;
}
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
while (TRUE) {
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);//創建socket並進行阻塞
char sendBuf[100] = { 0 };
sprintf_s(sendBuf, 100, "Welcome %s to bingo!", inet_ntoa(addrCli.sin_addr));
//發送數據
int iLen = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
if (iLen < 0) {
printf("send errorNum = %d\n", GetLastError()); return -1;
}
char recvBuf[100] = {0};
iLen = recv(sockConn, recvBuf, 100, 0); //接收數據
if (iLen < 0) {
printf("recv errorNum = %d\n", GetLastError());
return -1; }//打印接收的數據
printf("recvBuf = %s\n", recvBuf);
closesocket(sockConn);
}
}
創建Socket后調用bind()
綁定本機監聽地址。並且listen()
設置監聽連接請求。accept()
接受請求,並且進行阻塞。如果有會話連接過來可以調用recv()
和send()
進行接受和發送數據。
Client端
#include<iostream>
#include<WinSock2.h>
#include<stdio.h>
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
using namespace std;
#include<Windows.h>
#include<stdlib.h>
int main() {
printf("Client\n");
char sendBuf[] = "hello,world";
//1 初始化網絡庫 // 加載套接字庫
WORD wVersionRequested;
WSADATA wsaData; int err;
wVersionRequested = MAKEWORD(2, 2);
// 1、初始化套接字庫
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n", GetLastError()); WSACleanup();
return -1; }
// 新建套接字
SOCKET socket_cli = socket(AF_INET, SOCK_STREAM,0);
if (INVALID_SOCKET == socket_cli) {
cout << "Error" << endl;
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.8.104");
addrSrv.sin_port = htons(6001);
addrSrv.sin_family = AF_INET;
cout << "test" << endl;
if (connect(socket_cli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == SOCKET_ERROR) {
return -1;
}
// 4 接收和發送數據
char recvBuf[100] = { 0 };
int iLen = recv(socket_cli, recvBuf, 100, 0);
if (iLen < 0) {
printf("recv errorNum = %d\n", GetLastError());
return -1;
}
printf("Client recvBuf = %s\n", recvBuf);
iLen = send(socket_cli, sendBuf, strlen(sendBuf) + 1, 0);
if (iLen < 0) {
printf("send errorNum = %d\n", GetLastError());
return -1;
}
closesocket(socket_cli);
WSACleanup();
return 0;
}
UDP通訊
Server端
#include<iostream>
#include<WinSock2.h>
#include<stdio.h>
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
using namespace std;
#include<Windows.h>
#include<stdlib.h>
int main() {
// 初始化套接字庫
WORD wVersion; WSADATA wsaData;
int err; wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0) {
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return -1;
}// 創建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
cout << "socket創建成功" << endl;
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6003);
// 綁定套接字
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
// 等待並接收數據
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR_IN);
char recvBuf[100];
char sendBuf[100];
while (true) {
std::cout << "循環" << std::endl;
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
std::cout << recvBuf << std::endl;
sprintf_s(sendBuf, 100, "Ack %s", recvBuf);
sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
}closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
Client端:
int main() {
{ // 加載套接字庫
WORD wVersion;
WSADATA wsaData;
int err; wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0) {
return err;
}if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
WSACleanup(); return -1; }
// 創建套接字
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_port = htons(6001);
addrSrv.sin_family = AF_INET;
int len = sizeof(SOCKADDR); char sendBuf[] = "hello";
char recvBuf[100]; //發送數據
sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)& addrSrv, len);
recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)& addrSrv, &len);
std::cout << recvBuf << std::endl;
closesocket(sockCli);
system("pause"); return 0; }
}