Windows Sockets 2 笔记
文章目录
- 一、Winsock简介
- 二、Windows中Winsock对网络协议支持的情况
- 三、使用Winsock
- 3.1 关于服务器和客户端
- 3.2 创建基本Winsock应用程序
- 3.3 初始化Winscok
- 3.3.1 初始化步骤
- 3.3.2 初始化的核心代码
- 3.3.3 WSAStartup函数的协调
- 3.3.4 WSACleanup函数
- 3.3.5 初始化的完整代码
- 3.4 Winsock客户端应用程序
- 3.4.1 为客户端创建套接字
- 3.4.1.1 客户端创建套接字代码
- 3.4.1.2 getaddrinfo函数
- 3.4.1.3 socket函数
- 3.4.2 连接到套接字
- 3.4.3 在客户端上发送和接受数据
- 3.4.3.1 发送和接受数据的实现
- 3.4.3.2 send函数
- 3.4.3.3 recv函数
- 3.4.3.4 shutdown函数
- 3.4.3.5 closesocket函数
- 3.4.4 断开客户端的连接
- 3.4.5 完整的客户端实现
一、Winsock简介
MSDN原文链接。
Socket技术简介视频(看前半部分即可)。
Windows Sockets 2简写为Winsock,它的作用是使程序员能够创建高级 Internet(互联网) 、Intranet(内联网) 和其他种类支持网络的应用程序。
Winsock使得程序能够跨网络传输应用程序数据,并且独立于所使用的网络协议。
借助Winsock,程序员可以访问高级Microsoft Windows网络功能,例如多播和服务质量等。
从前的Winsock编程以TCP/IP协议为主,但使用TCP/IP的编程写法却不适用于其他类型的协议,因此Winsock API会根据需要添加函数来处理其他协议类型。
Windows Socket 2 为C/C++程序员设计,它可以在所有的Windows平台上使用,如果平台存在某些实现或功能限制,则会在文档中明确指明。
二、Windows中Winsock对网络协议支持的情况
MSDN原文链接。
Internet协议套件是企业网络和Internet中使用的主要网络协议。Internet协议套件表示分层网络协议的大型集合,它通常被称为TCP/IP,套件中包含的两个最重要的协议是:Internet协议(IP) 和 传输控制协议(TCP)。
IPv6 和 IPv4表示Internet协议的两个可用版本。TCP是重要的网络服务之一,通常将其称为通过IPv6 和 IPv4网络运行的IP协议。用户数据报协议(UDP) 和 Internet控制消息协议(ICMP) 是用于IPv6 和 IPv4网络的其他重要IP协议。因此可通过IPv6 和 IPv4网络使用多种IP协议。
Winsock将不同的网络协议套件视为不同的地址系列。如IPv6协议被视为AF_INET6地址系列,IPv4协议被视为AF_INET地址系列。IPv6 和 IPv4协议支持使用各种分层IP协议,例如TCP、UDP和ICMP。
三、使用Winsock
MSDN原文链接。
本节介绍Winsock编程技术,包括基本的Winsock编程技术和高级技术。
3.1 关于服务器和客户端
有两种不同类型的套接字(socket)网络应用程序:服务器 和 客户端。
服务器和客户端具有不同的行为,因此它们的代码是不同的。下面是用于创建 流式处理TCP/IP服务器和客户端 的常规模型。
| 服务器 | 客户端 |
| 1.初始化Winsock | |
| 2.创建套接字 | |
| 3.绑定套接字 | 3.连接到服务器 |
| 4.侦听客户端的套接字 | 4.发送和接受数据 |
| 5.接受来自客户端的连接 | 5.断开连接 |
| 6.接受和发送数据 | |
| 7.断开连接 | |
注意:表中客户端和服务器的步骤不具有对应性。
可以看出,客户端和服务器的处理模型中前两个步骤相同,这两个步骤的实现代码也几乎完全相同。
本指南中的某些步骤是特定于要创建应用程序的类型而实现的。
3.2 创建基本Winsock应用程序
步骤如下:
| 创建基本的Winsock应用程序 |
|---|
| 1.创建新的空项目,并将空的C++源文件添加到项目。 |
| 2.确保生成环境引用 Microsoft Windows软件开发工具包(SDK)。 |
| 3.确保生成环境链接到Winsock库文件Ws2_32.lib。使用Winsock的应用程序都必须与Ws2_32.lib库文件链接,可使用#pragma注释向链接器指示需要Ws2_32.lib文件。 |
| 4.开始对Winsock应用程序进行编程。包含Winsock2头文件即可使用Winsock API,Winsock2.h头文件包含了大多数Winsock的函数、结构和定义。包含Ws2tcpip.h头文件即可检索IP地址。 |
经过以上步骤得到的源代码如下:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>#pragma comment(lib, "Ws2_32.lib")int main() {return 0;
}
为了使用IP帮助应用程序使用API,需要包含lphlpapi.h头文件,并且其#include行应在Winsock.h头文件的#include行之后。如下所示:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h> // 放在winsock2.h之后
#include <stdio.h>#pragma comment(lib, "Ws2_32.lib")int main() {return 0;
}
Winsock2.h头文件包含了Windows.h头文件中的核心元素,因此一般Winsock应用程序无需#include< Windows.h >。
Windows.h中默认包含Windows socket 1.1版本的Winscok.h头文件。如果应用程序需要包含Windows.h头文件,那么为了避免Winsock.h中包含的声明与Window.h头文件相冲突,则应在包含Windows.h头文件之前定义WIN32_LEAN_AND_MEAN宏,它阻止了Windows.h包含Winsock.h。
同时包含windows.h和winsock.h头文件的正确示例如下:
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // 避免windows.h包含winsock 1.1
#endif#include <windows.h> // 先包含windows.h
#include <winsock2.h> // 再包含winsock2.h
#include <ws2tcpip.h>
#include <iphlpapi.h> // 最后包含iphlpapi.h#pragma comment(lib, "Ws2_32.lib") // 链接Winscok库int main() {return 0;
}
3.3 初始化Winscok
3.3.1 初始化步骤
调用Winsock函数的所有进程都必须在调用之前初始化Windows套接字DLL的使用。初始化过程可以确认Winsock版本是否在用户系统上受支持。
步骤一:创建WSADATA对象
WSADATA wsaData;
步骤二:调用WSAStartup并检查其返回的整数值。
3.3.2 初始化的核心代码
int iResult; // 存储整数返回值// 初始化Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {printf("WSAStartup failed: %d\n", iResult);return 1;
}
代码中WSAStartup的MSDN链接在此。
调用WSAStartup函数能启动WS2_32.dll的使用。WSADATA 结构包含有关 Windows 套接字实现的信息,WSAStartup会将传递的版本设置为调用方即用户可以使用的最高版本的Windows套接字支持。
WSAStartup参数中的MAKEWORD(2,2)指明该程序最高支持的Winsock版本为2.2,如果初始化成功,该函数返回零,否则将返回一个可查询的错误码。
WSAStartup函数必须是应用程序或DLL调用的第一个Winsock函数。它允许应用程序或DLL指定所需的Winsock版本,并检索特定Winsock实现的详细信息。
3.3.3 WSAStartup函数的协调
Windows Socket 2向后兼容,WSAStartup函数会对应用程序所期望的winsock版本 和 用户Winsock DLL所支持的winsock版本进行协调。
当应用程序或DLL调用WSAStartup函数时,Winsock DLL会检查函数参数中指定的Winsock版本,如果应用程序请求的版本即参数等于或高于Winsock DLL支持的最低版本,则调用成功。函数参数中的wsaData包含成员wVersion和wHighVersion,它们由Winsock DLL返回,分别代表协调结果 和 用户Winsock DLL所支持的最高版本。协调示例如下:

假设程序请求2.2版本的winsock,而用户Winsock DLL支持的最低和最高版本分别为1.1和2.0,则表明应用程序所请求版本区间为【1.0-2.2】,而用户Winsock DLL所支持版本区间为【1.1-2.0】。由于2.2大于1.1,则两者交集必然不为空,因此也就存在二者都接受的winsock版本,所以WSAStartup返回0表示初始化成功,并且参数wsaData中返回了协调的结果,wsaData.wHighVersion返回用户Winsock DLL所支持的最高版本即2.0,wsaData.wVersion返回二者都接受的winsock版本即 min(用户期望版本2.2,Winsock DLL最高支持版本2.0) = 2.0。
当前Winsock DLL即Ws2_32.dll支持的Winsock版本如下:
| Winsock版本 |
|---|
| 1.0 |
| 1.1 |
| 2.0 |
| 2.1 |
| 2.2 |
WSAStartup返回零仅表示有可用winscok版本,而不表示程序期望版本可用。如果你固定使用2.2版本的winsock,则在WSAStartup返回后,还需检查wsaData对象中的wVersion字段,因为如上例所示,它可能为2.0而非2.2,当且仅当它为2.2时才代表将使用2.2版本的Winscok。如果用户不支持较高如2.2版本的winsock,则可提醒用户安装更新版本的Winsock DLL。
3.3.4 WSACleanup函数
应用程序使用完Winsock DLL的服务后,必须调用WSACleanup,以允许Winsock DLL释放应用程序使用的内部Winsock资源。
如果需要多次获取WSADATA结构信息,应用程序可以多次调用WSAStartup。每次成功调用WSAStartup函数时,应用程序都必须调用WSACleanup函数,这意味着调用3次WSAStartup,则必须调用3次WSACleanup,而前两次WSACleanup除了递减内部计数器外什么也不做,最后一次WSACleanup调用才会将资源进行释放。
3.3.5 初始化的完整代码
WSAStartup函数通常会导致加载特定于协议的帮助程序DLL,因此不应从程序DLL中的DllMain函数调用它,否则可能导致死锁。
应用程序可以调用WSAGetLastError函数来确定winsock函数的扩展错误代码,它是Winsock 2.2 DLL中唯一可以在WSAStartup失败时调用的函数之一。
根据上述内容,初始化Winsock,并确保用户程序使用的Winsock版本是2.2,对应代码如下:
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif #include <windows.h>
#include <winSock2.h>
#include <ws2tcpip.h>
#include <stdio.h>#pragma comment(lib,"ws2_32.lib")int __cdecl main()
{WORD mAppVersion; // 存储应用程序期望使用的winsock版本WSADATA wsaData; int err;mAppVersion = MAKEWORD(2, 2); // 期望使用winsock 2.2err = WSAStartup(mAppVersion, &wsaData); // 初始化winscokif (err != 0) // 检查是否有可用版本{// 若返回非0表示错误,打印错误码printf("WSAStartup 错误的代码为:%d\n", err);return 1;}// 检查协调结果是否为winscok 2.2if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){// 如果不是winsock 2.2,则打印找不到winsock 2.2printf("找不到可用版本的Winsock.dll\n");// 由于成功调用WSAStartup,则应调用WSACleanupWSACleanup();return 1;}elseprintf("发现Winsock 2.2 dll正常\n");// 若协调结果为winsock 2.2 ,程序最后也应调用WSACleanupWSACleanup();
}
3.4 Winsock客户端应用程序
3.4.1 为客户端创建套接字
3.4.1.1 客户端创建套接字代码
回忆上文中的模型,服务器和客户端在初始化Winscok后都应创建套接字,但二者创建套接字的实现有所不同,现在介绍客户端创建套接字的实现。
初始化Winsock后,必须实例化SOCKET对象以供客户端使用。其具体实现如下:
// 为客户端创建套接字
{// addrinfo即地址信息类型struct addrinfo* result = NULL, * ptr, hints;ZeroMemory(&hints, sizeof(hints)); // 将hints的内存块全部置为零hints.ai_family = AF_INET; // 指明调用方支持的地址系列为IPv4hints.ai_socktype = SOCK_STREAM; // 支持的地套接字类型为流套接字hints.ai_protocol = IPPROTO_TCP; // 支持的协议类型为TCP#define DEFAULT_PORT "27015"// getaddrinfo函数提供与协议无关的从ANSI主机名到地址的转换iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);if (iResult != 0) // 检查是否成功转换(即返回零){// 失败则打印返回的非零Winsock错误代码printf("getaddrinfor 错误代码:%d\n", iResult);WSACleanup();return 1;}// 创建SOCKET对象SOCKET ConnectSocket = INVALID_SOCKET;ptr = result;// 调用socket函数并将其值返回到SOCKET对象中ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,ptr->ai_protocol);// 检查套接字是否有效if (ConnectSocket == INVALID_SOCKET){// 无效则打印错误号printf("Socket 错误代码:%ld\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();return 1;}
}
3.4.1.2 getaddrinfo函数
addrinfo是用来存储地址信息的结构。通过addrinfo结构和getaddrinfo函数即可保存主机的地址信息。
getaddrinfo 函数提供与协议无关的从 ANSI 主机名到地址的转换。
getaddrinfo函数的第一个参数:指向 以 NULL 结尾的 ANSI 字符串的指针,该字符串包含主机 (节点) 名称或数字主机地址字符串。 对于 Internet 协议,数字主机地址字符串是点十进制 IPv4 地址或 IPv6 十六进制地址。
getaddrinfo函数的第二个参数:指向以 NULL 结尾的 ANSI 字符串的指针,该字符串包含表示为字符串的服务名称或端口号。服务名称是端口号的字符串别名。 例如,“http”是由 Internet 工程任务组定义的端口 80 的别名, (IETF) 作为 Web 服务器用于 HTTP 协议的默认端口。 以下文件中列出了未指定端口号时此参数的可能值:%WINDIR%\system32\drivers\etc\services。
getaddrinfo函数的第三个参数:指向 addrinfo 结构的指针,该结构提供有关调用方支持的套接字类型的提示。此参数指向的 addrinfo 结构的ai_addrlen、ai_canonname、ai_addr和ai_next成员必须为零或 NULL。 否则, GetAddrInfoEx 函数将失败并 WSANO_RECOVERY。
getaddrinfo函数的第四个参数:指向包含有关主机的响应信息的一个或多个 addrinfo 结构的链接列表的指针。此参数返回的所有信息都是动态分配的,包括所有 addrinfo 结构、套接字地址结构和 addrinfo 结构指向的规范主机名字符串。 成功调用此函数分配的内存必须通过后续调用 freeaddrinfo 释放。
我们知道Socket是网络通信的一组API,服务器程序绑定到服务器主机IP地址的某个端口上,而客户端通过指定服务器的IP地址和端口向服务器发起连接请求。在getaddrinfo函数中,第一个参数可提供服务器的主机地址,第二个参数可提供服务器的端口号。
通过调用getaddrinfo函数,即可根据命令行上传递的服务器主机地址argv[1]和端口号参数,获得函数存储在result变量中的服务器地址信息。
3.4.1.3 socket函数
在上文代码中,我们首先创建了一个SOCKET对象并将其初始化为INVALID_SOCKET,这个枚举值表明此SOCKET对象的值无效。
后续我们调用了socket函数,通过保存它的返回值为客户端创建了套接字。
socket函数的三个参数分别为:地址系列规范、新套接字的类型规范、要使用的协议。如果未发生错误,则socket将返回引用新套接字的描述符,否则返回INVALID_SOCKET,并且可以通过调用WSAGetLastError来检索特定的错误代码。
要重视错误检测,它是成功网络代码的关键部分。如果socket调用失败,将返回INVALID_SOCKET。我们将判断返回的SOCKET对象是否正确,若不正确则会调用WSAGetLastError,它会返回与上次发生错误相关联的错误号。
可能需要进行更广泛的错误检查,即针对程序代码的检查,例如将hints.ai_family设置为AF_UNSPEC可能会导致连接调用失败,如果发生这种情况将其改为特定的IPv4或IPv6即可。
WSACleanup将终止WS2_32 DLL的使用。
3.4.2 连接到套接字
为客户端创建套接字后,要使得客户端能在网络上进行通信,则必须将其连接到服务器。
调用connect函数,将创建的套接字即SOCKET对象和sockaddr结构作为参数传入,最后检查常规错误。实现代码如下:
// 连接到服务器
iResult = connect(ConnectSocket, ptr->ai_addr,(int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{ // 如果连接失败则关闭socket并置SOCKET对象为无效值closesocket(ConnectSocket);ConnectSocket = INVALID_SOCKET;
}// 无论是否成功连接,都释放返回的服务器地址信息
freeaddrinfo(result);// 检查是否成功连接,若失败则打印连接失败
if (ConnectSocket == INVALID_SOCKET)
{printf("无法连接到服务器!\n");WSACleanup();return 1;
}
注意ptr仅为result中的第一个指针,如果连接调用失败,应该尝试getaddrinfo返回的下一个地址,代码如下:
// 为客户端创建套接字// addrinfo即地址信息类型
struct addrinfo* result = NULL, * ptr, hints;ZeroMemory(&hints, sizeof(hints)); // 将hints的内存块全部置为零
hints.ai_family = AF_INET; // 指明调用方支持的地址系列为IPv4
hints.ai_socktype = SOCK_STREAM; // 支持的地套接字类型为流套接字
hints.ai_protocol = IPPROTO_TCP; // 支持的协议类型为TCP#define DEFAULT_PORT "27015"// getaddrinfo函数提供与协议无关的从ANSI主机名到地址的转换
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if (iResult != 0) // 检查是否成功转换(即返回零)
{// 失败则打印返回的非零Winsock错误代码printf("getaddrinfor 错误代码:%d\n", iResult);WSACleanup();return 1;
}// 创建SOCKET对象
SOCKET ConnectSocket = INVALID_SOCKET;ptr = result;// 调用socket函数并将其值返回到SOCKET对象中
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,ptr->ai_protocol);// 检查套接字是否有效
if (ConnectSocket == INVALID_SOCKET)
{// 无效则打印错误号printf("Socket 错误代码:%ld\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();return 1;
}while (ptr != NULL) // 只要指向服务器地址信息的指针不为空
{// 就尝试连接服务器iResult = connect(ConnectSocket, ptr->ai_addr,(int)ptr->ai_addrlen);// 如果连接失败则更换服务器的下一个地址信息,成功则退出循环if (iResult == SOCKET_ERROR)ptr = ptr->ai_next;elsebreak;
}// 检查是否连接成功,即最后一次连接是否返回非SOCKET_ERROR值
if (iResult == SOCKET_ERROR)
{// 如果连接服务器失败,则关闭socket,并将SOCKET对象设为无效值closesocket(ConnectSocket);ConnectSocket = INVALID_SOCKET;
}// 无论成功与否,连接服务器完毕则释放动态分配的服务器地址信息result
freeaddrinfo(result);// 检查最后是否连接到服务器,没有则打印提示
if (ConnectSocket == INVALID_SOCKET)
{printf("无法连接到服务器!\n");WSACleanup();return 1;
}
getaddrinfo函数确定了sockaddr结构中的值,sockaddr结构中指定的信息包括:
- 客户端将尝试连接到的服务器的IP地址。
- 客户端将连接到的服务器上的端口号。
getaddrinfo函数会返回一个addinfo链表,如果对第一个IP地址的连接失败,则请尝试下一个addrinfo结构。
3.4.3 在客户端上发送和接受数据
3.4.3.1 发送和接受数据的实现
下面的代码将展示客户端成功连接服务器后,如何使用send和recv函数实现在客户端上收发数据。
#define DEFAULT_BUFLEN 512 // 定义默认缓冲区长度// 将接收缓冲区的长度设置默认值
int recvbuflen = DEFAULT_BUFLEN;// 定义客户端要发送给服务器的字符串
const char* sendbuf = "this is a test";
// 定义接受服务器信息的接受缓冲区
char recvbuf[DEFAULT_BUFLEN];// 向服务器发送信息
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);// 检查是否发送成功,否则打印错误代码并关闭SOCK结束程序
if (iResult == SOCKET_ERROR)
{printf("发送错误代码:%d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;
}// 打印send函数的返回值,即发送的字节数
printf("Bytes Send:%ld\n", iResult);// 禁用套接字对象的发送操作
iResult = shutdown(ConnectSocket, SD_SEND);// 查看禁用是否成功
if (iResult == SOCKET_ERROR)
{printf("shutdown faild:%d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;
}// 循环接受服务器的消息
do {// 接受服务器的消息并存储在recvbuf中iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);// 如果接受成功则打印接收到字节数if (iResult > 0)printf("Bytes received:%d\n", iResult);else if (iResult == 0) // 返回零表示连接断开printf("Connection closed\n");else // 返回负值表示错误代码printf("recv failed:%d\n", WSAGetLastError());
} while (iResult > 0); // 当且仅当接收成功时继续循环
上述代码很容易理解,但我们不妨来深入了解以下其中涉及的winsock API即send、shutdown和recv。
3.4.3.2 send函数
send函数的功能是在连接的套接字上发送数据,其函数定义如下:

如果未发生错误,send将返回发送的总字节数,该字节数可能小于len参数中请求发送的数量。如果发生错误则返回SOCKET_ERROR,并且可以通过调用WSAGetLastError检索特定的错误代码。
3.4.3.3 recv函数
recv函数从连接的套接字或绑定的无连接套接字上接收数据。其函数定义如下:

如果未发生错误,则recv函数返回接收到的字节数,buf参数指向的缓冲区将包含接收到的此数据。如果连接已正常关闭,则recv函数返回值为零。如果发生错误,返回SOCKET_ERROR,并且可通过调用 WSAGetLastError 来检索错误代码。
3.4.3.4 shutdown函数
shutdown函数可禁用套接字的发送或接收等操作。其定义如下:

如果未发生错误,则shutdown将返回零。否则返回SOCKET_ERROR,并且可通过调用 WSAGetLastError 来检索错误代码。
3.4.3.5 closesocket函数
closesocket函数关闭现有的套接字,如果未发生错误则返回零,否则返回SOCKET_ERROR,并且可通过调用 WSAGetLastError 来检索错误代码。
对于每次成功调用套接字,应用程序应始终具有匹配的 closesocket 调用,以将任何套接字资源返回到系统。
3.4.4 断开客户端的连接
客户端完成发送和接收数据后,客户端应该与服务器断开连接并关闭套接字。以下根据应用程序所处的两种情况,对断开连接进行实现。
(1)当客户端完成发送数据的操作后,可以调用shutdown函数并指定SD_SEND关闭套接字的发送端,这将允许服务器释放此套接字的某些资源,并且客户端应用程序仍可接收套接字上的数据。实现如下:
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{printf("shutdown faild:%d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;
}
(2)当客户端完成接收数据后,应调用closesocket函数以关闭套接字。应用程序若使用完Winsock DLL时,应使用WSACleanup函数来释放Winscok的所有资源,因此实现如下:
closesocket(ConnectSocket);
WSACleanup();return 0;
3.4.5 完整的客户端实现
完整的实现如下:
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif #include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"Mswsock.lib")
#pragma comment(lib,"AdvApi32.lib")#define DEFAULT_BUFLEN 512 // 定义默认缓冲大小
#define DEFAULT_PORT "27015" // 定义服务器端口int __cdecl main(int argc,char **argv)
{WSADATA wsaData; // 保存winsock版本信息SOCKET ConnectSocket = INVALID_SOCKET; // SOCKET对象struct addrinfo* result = NULL, // 存储服务器地址信息* ptr = NULL, hints;const char* sendbuf = "this is a test"; // 存储客户端发送给服务器的字符串char recvbuf[DEFAULT_BUFLEN]; // 定义接收缓冲区int iResult; int recvbuflen = DEFAULT_BUFLEN; // 接收缓冲区大小为默认值if (argc != 2) // 如果程序命令行不包含第二个参数,即不包含服务器主机的IP地址{// 则提示并结束程序printf("程序%s命令行中未使用服务器名称\n", argv[0]);return 1;}// 初始化WinsockiResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (iResult != 0){// 检查是否初始化成功,否则打印错误码,并结束程序printf("WSAStartup 错误码:%d\n", iResult);return 1;}// 这里可以检查wsaData.wVersion纪录的版本是否为2.2,以确保winsock DLL 2.2版本可用 // 将hints内存置零ZeroMemory(&hints, sizeof(hints));// hints记录了调用方即用户支持的套接字类型hints.ai_family = AF_UNSPEC; // 地址系列的支持未指定hints.ai_socktype = SOCK_STREAM; // 套接字类型支持流传输套接字hints.ai_protocol = IPPROTO_TCP; // 套接字协议支持TCP协议// 获取服务器的地址信息iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);if (iResult != 0){// 如果获取失败则打印错误代码// 由于初始化winsock成功,因此要调用WSACleanup,结束程序printf("getaddrinfo 错误码:%d\n", iResult);WSACleanup();return 1;}// 当获取服务器地址信息成功后,遍历服务器的每一个地址信息for (ptr = result; ptr != NULL; ptr = ptr->ai_next){// 使用服务器的地址信息创建客户端的套接字ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,ptr->ai_protocol);// 检查套接字是否创建成功if (ConnectSocket == INVALID_SOCKET){// 失败则打印错误码printf("socket 错误码:%ld\n", WSAGetLastError());WSACleanup();return 1;}// 成功创建套接字,则连接服务器iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);// 检查连接服务器是否成功if (iResult == SOCKET_ERROR){// 由于成功创建套接字,所以连接失败需要关闭socketclosesocket(ConnectSocket);// 将套接字重置为无效值,遍历服务器的下一个地址信息ConnectSocket = INVALID_SOCKET;continue;}// 连接服务器成功则退出遍历break;}// 无论连接服务器成功与否,释放服务器地址信息freeaddrinfo(result);// 判断是否连接成功,否则打印提示信息if (ConnectSocket == INVALID_SOCKET){printf("无法连接到服务器!\n");WSACleanup();return 1;}// 连接成功则向服务器发送数据iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);if (iResult == SOCKET_ERROR){// 检查是否发送成功,否则关闭套接字,并结束程序printf("send 错误码: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;}printf("发送的字节数为: %ld\n", iResult);// 禁用客户端发送操作iResult = shutdown(ConnectSocket, SD_SEND);if (iResult == SOCKET_ERROR) {// 检查禁用是否成功,否则关闭套接字,并结束程序printf("shutdown 错误码: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;}// 禁用成功则循环接收服务器消息do {// 接收服务器消息iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);if (iResult > 0) // 接收成功printf("接收的字节数为: %d\n", iResult);else if (iResult == 0) // 连接关闭则提示printf("连接已关闭\n");else // 接收失败则打印错误信息printf("recv 错误码: %d\n", WSAGetLastError());} while (iResult > 0); // 重复接收// 最后若连接正常关闭,则关闭套接字并结束程序closesocket(ConnectSocket);WSACleanup();return 0;
}
相关文章:
Windows Sockets 2 笔记
文章目录 一、Winsock简介二、Windows中Winsock对网络协议支持的情况三、使用Winsock3.1 关于服务器和客户端3.2 创建基本Winsock应用程序3.3 初始化Winscok3.3.1 初始化步骤3.3.2 初始化的核心代码3.3.3 WSAStartup函数的协调3.3.4 WSACleanup函数3.3.5 初始化的完整代码 3.4 …...
13章总结
一.泛型 1.定义泛型类 泛型机制语法: 类名<T> 其中,T是泛型的名称,代表某一种类型。 【例13.6】创建带泛型的图书类 代码: 结果: 2.泛型的常规用法 (1)定义泛型类时声明多个变量 class MyClass<T1,T2>…...
(2023,3D NeRF,无图像变分分数蒸馏,单步扩散)SwiftBrush:具有变分分数蒸馏的一步文本到图像扩散模型
SwiftBrush : One-Step Text-to-Image Diffusion Model with Variational Score Distillation 公众:EDPJ(添加 VX:CV_EDPJ 或直接进 Q 交流群:922230617 获取资料) 目录 0. 摘要 1. 方法 1.1 基础 1.2 SwiftBrus…...
【WPF.NET开发】将路由事件标记为已处理和类处理
本文内容 先决条件何时将路由事件标记为已处理预览和浮升路由事件对实例和类路由事件处理程序复合控件中的输入事件禁止 尽管对于何时将路由事件标记为已处理没有绝对规则,但如果代码以重要方式响应事件,请考虑将事件标记为已处理。 标记为已处理的路由…...
2023年03月18日_微软office365 copilot相关介绍
文章目录 Copilot In WordCopilot In PowerpointCopilot In ExcelCopilot In OutlookCopilot In TeamsBusiness Chat1 - copilot in word2 - copilot in excel3 - copilot in powerpoint4 - copilot in outlook5 - copilot in teams6 - business chat word 1、起草草稿 2、自动…...
GBASE南大通用携手宇信科技打造“一表通”全链路解决方案
什么是“一表通”? “一表通”是国家金融监督管理总局为发挥统计监督效能、完善银行保险监管统计制度、推进监管数据标准化建设、打破数据壁垒,而制定的新型监管数据统计规范。相较于以往的报送接口,“一表通”提高了对报送时效性、校验准确…...
Python 内置高阶函数练习(Leetcode500.键盘行)
Python 内置高阶函数练习(Leetcode500.键盘行) 【一】试题 (1)地址: 500. 键盘行 - 力扣(LeetCode) (2)题目 给你一个字符串数组 words ,只返回可以使用在…...
【JavaWeb】day01-HTMLCSS
day01-HTML&CSS HTML 图片标签:<img> src:指定图像URL(绝对路径/相对路径)width:图像宽度(像素/相对于父元素的百分比)height:图像高度(像素/相对于父元素的百…...
【工具】windeployqt 在windows + vscode环境下打包
目录 0.背景简介 1.windeployqt简介 2.打包具体过程 1)用vscode编译,生成Release文件夹(也有Debug文件夹,但是发布版本一般都是用Release) 2)此时可以看下Release文件夹内,一般是.exe可执行…...
跟着LearnOpenGL学习12--光照贴图
文章目录 一、前言二、漫反射贴图三、镜面光贴图3.1、采样镜面光贴图 一、前言 在跟着LearnOpenGL学习11–材质中,我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观…...
DotNet 命令行开发
DotNet 命令行开发 下载安装下载 SDK安装 SDK绿色版下载绿化脚本 常用命令创建 dotnet new运行 dotnet run发布应用 dotnet publish更多命令 VSCode 调试所需插件调试 CS 配置项目.csproj排除依赖关系 launch.jsontasks.json 参考资料 下载安装 下载 SDK 我们就下最新的好&am…...
hyperf console 执行
一、原理描述 hyperf中,不难发现比如自定义控制器中获取参数,hyperf.php中容器获取,传入的都是接口,而不是实体类。 这是因为框架中的配置文件有设置对应抽象类的子类,框架加载的时候将其作为数组,使用的…...
第一篇 设计模式引论 - 探索软件设计的智慧结晶
1. 设计模式的定义和起源 设计模式,这个术语最初在建筑领域被广泛使用,用来描述在建筑设计中反复出现的问题及其解决方案。在软件工程中,设计模式同样指的是在软件设计过程中反复出现的、经过验证的最佳实践和解决方案。 1994年,…...
HBase基础知识(六):HBase 对接 Hive
1. HBase 与 Hive 的对比 1.Hive (1) 数据仓库 Hive 的本质其实就相当于将 HDFS 中已经存储的文件在 Mysql 中做了一个双射关系,以 方便使用 HQL 去管理查询。 (2) 用于数据分析、清洗 Hive 适用于离线的数据分析和清洗,延迟较高。 (3) 基于…...
Java连接Mysql报错:javax.net.ssl.SSLException: Received fatal alert: internal_error
大致报错日志如下: The last packet successfully received from the server was 11 milliseconds ago. The last packet sent successfully to the server was 10 milliseconds ago.at sun.reflect.GeneratedConstructorAccessor275.newInstance(Unknown Source)…...
Mixtral 8*7B + Excel + Python 超强组合玩转数据分析
Mixtral 8*7B Excel Python 超强组合玩转数据分析 0. 背景1. 使用 Mixtral 8*7B pandas 实现数据导入和导出1.1 使用 Mixtral 8*7B pandas 导入 Excel 文件中的数据1.2 使用 Mixtral 8*7B pandas 导出 Excel 文件中的数据 2. 使用 Mixtral 8*7B pandas 实现单个文件数据的…...
深入浅出理解Web认证:Session、Cookie与Token
在Web开发的世界中,理解Session、Session ID、Cookie和Token之间的区别至关重要。实际上,这些概念并不复杂,只需几句话就能澄清它们的核心区别。 首先,我们需要区分Session和Session ID。Session实际上是存储在服务器端的数据&am…...
智慧零售技术探秘:关键技术与开源资源,助力智能化零售革新
智慧零售是一种基于先进技术的零售业态,通过整合物联网、大数据分析、人工智能等技术,实现零售过程的智能化管理并提升消费者体验。 实现智慧零售的关键技术包括商品的自动识别与分类、商品的自动结算等等。 为了实现商品的自动识别与分类,…...
2012年第一届数学建模国际赛小美赛B题大规模灭绝尚未到来解题全过程文档及程序
2012年第一届数学建模国际赛小美赛 B题 大规模灭绝尚未到来 原题再现: 亚马逊是地球上现存最大的雨林,比地球上任何地方都有更多的野生动物。它位于南美洲大陆的北侧,共有9个国家:巴西、玻利维亚、厄瓜多尔、秘鲁、哥伦比亚、委…...
macos管理本地golang的多版本sdk
背景 无论你是哪个编程语言的开发者,例如 Java、Go 等,通常在本地开发过程中,你经常需要安装相应的 SDK。由于各种原因,往往需要在不同的项目中来回切换多个版本的 SDK。 安装步骤 1.安装homebrew /bin/bash -c "$(curl -…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
