1. 程式人生 > 其它 >Windows程式設計(網路程式設計)

Windows程式設計(網路程式設計)

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_INETPF_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_DGRAMSOCK_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.hWsrm.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_UNSPECAF_INETAF_INET6並且型別引數為SOCK_RAW或未指定時,這是一個可能的值。Windows XP 及更高版本支援此協議值。
IPPROTO_IGMP Internet 組管理協議 (IGMP)。當af引數為AF_UNSPECAF_INETAF_INET6並且型別引數為SOCK_RAW或未指定時,這是一個可能的值。Windows XP 及更高版本支援此協議值。
BTHPROTO_RFCOMM 藍芽射頻通訊 (Bluetooth RFCOMM) 協議。當af引數為AF_BTH型別引數為SOCK_STREAM時,這是一個可能的值。帶有 SP2 或更高版本的 Windows XP 支援此協議值。
IPPROTO_TCP 傳輸控制協議 (TCP)。當af引數為AF_INETAF_INET6並且型別引數為SOCK_STREAM時,這是一個可能的值。
IPPROTO_UDP 使用者資料報協議 (UDP)。當af引數是AF_INETAF_INET6並且型別引數是SOCK_DGRAM時,這是一個可能的值。
IPPROTO_ICMPV6 Internet 控制訊息協議版本 6 (ICMPv6)。當af引數為AF_UNSPECAF_INETAF_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; }
}

TCP與UDP適用場景