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;
}