Windows套接字2 winsock2 的使用,服务器端

Windows套接字2
简单了解了一下Win32下的Socket使用,以下为服务器端应用程序的学习笔记与简单案例,留作记录。

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
//Windows.h 标头默认包含 Windows Sockets 1.1 的 Winsock.h 头文件
//Winsock.h 头文件中的声明将与 Windows Sockets 2.0 所需的 Winsock2.h 头文件中的声明冲突
//WIN32_LEAN_AND_MEAN宏可防止 Windows.h 标头包含 Winsock.h

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>

//使用 Winsock 的应用程序必须与 Ws2_32.lib 库文件链接
#pragma comment(lib, "Ws2_32.lib")

int main() {
	WSADATA wsaData;
	int iResult;

	///初始化 Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed: %d\n", iResult);
		return 1;
	}

	///为服务器创建套接字
#define DEFAULT_PORT "27015"

	struct addrinfo *result = NULL, *ptr = NULL, hints;

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;

	//解析服务器要使用的本地地址和端口
	iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
	//AF_INET 用于指定 IPv4 地址系列。
	//SOCK_STREAM 用于指定流套接字。
	//IPPROTO_TCP 用于指定 TCP 协议。
	//AI_PASSIVE 标志指示调用方打算在对 绑定 函数的调用中使用返回的套接字地址结构。 
	//当将AI_PASSIVE标志设置为 getaddrinfo 函数的 nodename 参数为 NULL 指针时,
	//套接字地址结构的 IP 地址部分设置为INADDR_ANY IPv4 地址或 IPv6 地址的IN6ADDR_ANY_INIT。
	//27015 是与客户端将连接到的服务器关联的端口号。
	if (iResult != 0) {
		printf("getaddrinfo failed: %d\n", iResult);
		WSACleanup();
		return 1;
	}

	//创建一个名为 ListenSocket 的 SOCKET 对象,供服务器侦听客户端连接。
	SOCKET ListenSocket = INVALID_SOCKET;

	//调用 socket 函数并将其值返回到 ListenSocket 变量
	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

	//检查错误以确保套接字是有效的套接字。
	if (ListenSocket == INVALID_SOCKET) {
		printf("Error at socket(): %ld\n", WSAGetLastError());
		freeaddrinfo(result);
		WSACleanup();
		return 1;
	}

	///绑定套接字
	//设置TCP侦听套接字
	iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
	if (iResult == SOCKET_ERROR) {
		printf("bind failed with error: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}

	//调用 bind 函数后,不再需要 getaddrinfo 函数返回的地址信息。 
	//调用 freeaddrinfo 函数以释放 getaddrinfo 函数为此地址信息分配的内存。
	freeaddrinfo(result);

	///侦听套接字
	//套接字绑定到系统上的 IP 地址和端口后,服务器必须侦听该 IP 地址和端口以获取传入连接请求。
	//调用 listen 函数,以参数的形式传递所创建的套接字和积压工作的值、要接受的挂起连接队列的最大长度。 
	//在此示例中, 积压工作 参数设置为 SOMAXCONN。 
	//此值是一个特殊常量,指示此套接字的 Winsock 提供程序允许队列中最大合理数量的挂起连接。
	if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
		printf("Listen failed with error: %ld\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}

	///接受连接 (Windows套接字 2)
	//创建名为 ClientSocket 的临时 SOCKET 对象,用于接受来自客户端的连接。
	SOCKET ClientSocket;

	//通常,服务器应用程序设计为侦听来自多个客户端的连接。 
	//对于高性能服务器,通常使用多个线程来处理多个客户端连接。

	//使用 Winsock 有多种不同的编程技术可用于侦听多个客户端连接。 
	//一是创建一个连续循环,该循环使用 listen 函数检查连接请求, (看到 套接字上的侦听) 。
	//如果发生连接请求,应用程序将调用 accept、 AcceptEx 或 WSAAccept 函数,并将工作传递给另一个线程来处理请求。
	//请注意,此基本示例非常简单,不使用多个线程。 该示例还仅侦听并仅接受单个连接。

	ClientSocket = INVALID_SOCKET;

	//接受客户端连接
	ClientSocket = accept(ListenSocket, NULL, NULL);
	if (ClientSocket == INVALID_SOCKET) {
		printf("accept failed: %d\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}

	///在套接字上接收和发送数据
#define DEFAULT_BUFLEN 512

	char recvbuf[DEFAULT_BUFLEN];
	int /*iResult,*/ iSendResult;
	int recvbuflen = DEFAULT_BUFLEN;

	//接收,直到客户端关闭连接
	do {

		iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
		if (iResult > 0) {
			printf("Bytes received: %d\n", iResult);

			//将缓冲区内容给发送方
			iSendResult = send(ClientSocket, recvbuf, iResult, 0);
			if (iSendResult == SOCKET_ERROR) {
				printf("send failed: %d\n", WSAGetLastError());
				closesocket(ClientSocket);
				WSACleanup();
				return 1;
			}
			printf("Bytes sent: %d\n", iSendResult);
		}
		else if (iResult == 0)
			printf("Connection closing...\n");
		else {
			printf("recv failed: %d\n", WSAGetLastError());
			closesocket(ClientSocket);
			WSACleanup();
			return 1;
		}

	} while (iResult > 0);
	//send 和 recv 函数分别返回 发送或接收的字节数的整数值 或 错误。 
	//每个函数还采用相同的参数:活动套接字、缓冲区、要发送或接收的字节数以及要使用的任何标志。

	///断开服务器连接
	//服务器将数据发送到客户端后,可以调用 shutdown 函数,指定SD_SEND关闭套接字的发送端。 
	//这样,客户端就可以释放此套接字的某些资源。 服务器应用程序仍可接收套接字上的数据。
	iResult = shutdown(ClientSocket, SD_SEND);
	if (iResult == SOCKET_ERROR) {
		printf("shutdown failed: %d\n", WSAGetLastError());
		closesocket(ClientSocket);
		WSACleanup();
		return 1;
	}

	//清理资源
	closesocket(ClientSocket);
	WSACleanup();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值