1. 关于 Servers 和 Clients

有两种不同类型的套接字网络应用程序:服务器和客户端。

服务器和客户端的行为不同;因此,创建它们的流程也是不一样的。以下是用于创建流式 TCP/IP 服务器和客户端的一般模型。

1.1 Server

  1. Initialize Winsock.

  2. Create a socket.

  3. Bind the socket.

  4. Listen on the socket for a client.

  5. Accept a connection from a client.

  6. Receive and send data.

  7. Disconnect.

1.2 Client

  1. Initialize Winsock.

  2. Create a socket.

  3. Connect to the server.

  4. Send and receive data.

  5. Disconnect.

2. Create a Basic Winsock Application

创建一个基本的 Winsock 应用需要基于以下条件:

  1. 确保编译环境可以引用 SDK 的 include,Lib 和 Src 目录。

  2. 确保编译环境可以链接到 Winsock 库文件(Ws2_32.lib)。可以通过 #pragma comment(lib, “ws2_32.lib”) 显式的链接。

  3. 编程开始前,使用 Winsock API 要包含 Winsock2.h 头文件,它包含了常用的 Winsock 的函数,结构体和定义。Ws2tcpip.h 头文件包含 WinSock 2 协议特定附件文档中针对 TCP/IP 引入的定义,其中包括用于检索 IP 地址的较新的函数和结构。

#include <winsock2.h>
#include <ws2tcpip.h>#pragma comment(lib, "Ws2_32.lib")int main()
{return 0;
}

Note: 如果需要使用 IP Helper APIs,需要包含 lphlpapi.h 头文件,但必须置于 Winsock2.h 头文件之后。 Winsock2.h 头文件内部包含了 Windows.h 中的核心元素,因此不需要再包含 Windows.h 头文件。如果需要包含 Windows.h 头文件,则需要定义宏  #define WIN32_LEAN_AND_MEAN。由于历史原因,Windows.h 标头默认包含 Windows Socket 1.1 的 Winsock.h 头文件。Winsock.h 头文件中的声明将与 Windows Socket 2.0 所需的Winsock2.h 头文件中的声明冲突。WIN32_LEAN_AND_MEAN 宏可防止 Winsock.h 包含在 Windows.h 头文件中。下面示例了一个说明这一点。

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>#pragma comment(lib, "Ws2_32.lib")int main()
{return 0;
}

3. Initializing Winsock

调用 Winsock 函数的所有进程(应用或者 DLLs)都必须先初始化 Windows Sockets DLL。

这也用于确认系统是否支持 Winsock。

3.1 初始化 Winsock

  1. 创建一个 WSADATA 对象,名为 wsaData。
WSADATA wsaData;
  1. 调用 WSAStartup 函数并检查其返回值。
int iResult;// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0)
{printf("WSAStartup failed: %d\n", iResult);return 1;
}

调用 WSAStartup 函数以启用 WS2_32.dll。

WSADATA 结构体包含 Windows Sockets 的信息。WSAStartup 的 MAKEWORD(2,2) 参数在系统上发出对 Winsock 版本 2.2 的请求,并将传递的版本设置为调用方可以使用的最高版本的 Windows Sockets 支持。

4A. Winsock server application

4A.1 Create a Socket for Server

在初始化后,必须实例化 SOCKET 对象以供服务器使用。

4A.1.1 To create a socket for the server

  1. [getaddrinfo](getaddrinfo function (ws2tcpip.h) - Win32 apps | Microsoft Learn) 函数用于决定 [sockaddr](sockaddr - Win32 apps | Microsoft Learn) 结构体中的值:
  • AF_INET 用于指定 IPv4 地址家族。

  • SOCK_STREAM 用于指定一个流 socket。

  • IPPROTO_TCP 用于指定 TCP 协议。

  • AI_PASSIVE flag 表明调用方打算在调用 bind 函数时使用返回 socket 地址结构。当 AI_PASSOVE flag 置位,并传给 getaddrinfo 函数的 nodename 参数为 NULL 指针,对于 IPv4 地址,socket 地址结构的 IP 地址部分为 INADDR_ANY。 而 IPv6 地址,socket 地址结构的 IP 地址部分为 IN6ADDR_ANY_INIT。

  • 27015 是与客户端将连接到的服务器关联的端口号。

// addrinfo 结构体被 getaddrinfo 函数使用。
#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;// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{printf("getaddrinfo failed: %d\n", iResult);WSACleanup();return 1;
}
  1. 创建一个 SOCKET 对象,名为 ListenSocket,用于服务器监听客户端的连接。
SOCKET ListenSocket = INVALID_SOCKET;
  1. 调用 socket 函数,ListenSocket 变量用于接收函数返回值。对于这个服务器应用程序,采用 getaddrinfo 函数返回的第一个与 hints 参数设置相匹配的 IP 地址。

如果服务器应用程序希望监听 IPv6,则将 hints.ai_family = AF_INET6 即可。如果一个服务器希望同时监听 IPv4 和 IPv6,则必须创建两个监听 sockets。一个 IPv4,一个 IPv6,并且应用程序必须单独处理这两个 sockets 。

// Create a SOCKET for the server to listen for client connections
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
  1. 检查 socket,确保创建的 socket 有效。
if (ListenSocket == INVALID_SOCKET)
{printf("Error at socket(): %ld\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();return 1;
}

4A.2 Binding a Socket

要使服务器接受客户端连接,它必须绑定到系统中的网络地址。客户端应用程序使用 IP 地址和端口连接到主机网络。

sockaddr 结构体保存着 address family, IP address 和 port number。

调用 bind 函数,传入已创建的 socket 和 从 getaddrinfo 函数返回的sockaddr 结构体。

// Setup the TCP listening socket
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;
}

Note: 一旦 bind 函数被调用,getaddrinfo 函数返回的 address 信息将不再需要。调用 freeaddrinfo 函数释放 getaddrinfo 函数分配的内存空间。

freeaddrinfo(result);

4A.3 Listening on a Socket

将 socket 绑定到系统上的 IP 地址和端口后,服务器必须侦听该 IP 地址和端口以接收传入的连接请求。

调用 listen 函数,将创建的 socket 和 blacklog(要接受的挂起连接的队列的最大长度)值作为参数传递给 listen 函数。以下示例中,backlog 值设置为 SOMAXCONN。该值是一个特殊常量,指示 Winsock 提供程序为该 socket 允许队列中最大数量的挂起连接。检查常规错误的返回值。

if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR )
{printf( "Listen failed with error: %ld\n", WSAGetLastError() );closesocket(ListenSocket);WSACleanup();return 1;
}

4A.4 Accepting a Connection

socket 监听连接后,程序必须处理该 socket 上的连接请求。

4A.4.1 To accept a connection on a socket

  1. 创建一个临时的 SOCKET 对象,名为 ClientSocket,用于接收来自客户端的连接请求。
SOCKET clientSocket;
  1. 通常,服务器应用程序将设计为监听来自多个客户端的连接。对于高性能服务器,通常使用多个线程来处理多个客户端连接。

使用 Winsock 的几种不同的编程技术可用于监听多个客户端连接。一种编程技术是创建一个连续循环,该循环使用 listen 函数检查连接请求(请参阅 Listening on a Socket)。如果发生了一个连接请求,应用程序调用 accept, AcceptEx,或 WSAAccept 函数,并将工作传递给另一个线程来处理该请求。还有其他几种编程技术是可能的。

Note: 此示例非常简单,不使用多个线程。该示例还仅监听并仅接受单个连接。


ClientSocket = INVALID_SOCKET;// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET)
{printf("accept failed: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;
}
  1. 当接收到客户端连接,服务器应用程序通常会将接受的客户端 socket(上述示例代码中的 ClientSocket 变量)传递给工作线程或 I/O completion port,并继续接受其他连接。

还有许多其他编程技术可用于监听和接收多个连接。这些包括使用 select 或 WSAPoll 函数。微软 Windows 软件开发工具包 (SDK) 附带的 Advanced Winsock Samples 中演示了其中一些各种编程技术的示例。

Note: 在 Unix 系统上,服务器的一种常见编程技术是让应用程序监听连接。当连接被接收时,父进程将调用 fork 函数来创建新的子进程来处理客户端连接,并从父进程继承 socket。Windows 不支持此编程技术,因为不支持 fork 函数。此技术通常也不适用于高性能服务器,因为创建新进程所需的资源远远大于线程所需的资源。

4A.5 Receiving and Sending Data on the Server

以下示例使用 recv 和 send 函数接收来自客户端的请求和发送服务器端的响应。

#define DEFAULT_BUFLEN 512char recvbuf[DEFAULT_BUFLEN];
int iResult, iSendResult;
int recvbuflen = DEFAULT_BUFLEN;// Receive until the peer shuts down the connection
do {iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);if (iResult > 0) {printf("Bytes received: %d\n", iResult);// Echo the buffer back to the senderiSendResult = 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 函数都分别返回发送或接收的字节数的整数值,或返回错误。每个函数还采用相同的参数:active socket、char buffer、要发送或接收的字节数以及要使用的 flag。

4A.6 Disconnecting the Server

一旦服务器完成从客户端接收数据并将数据发送回客户端,服务器就会断开与客户端的连接并关闭 socket。

4A.6.1 To disconnect and shutdown a socket

  1. 当服务器完成向客户端发送数据时,可以调用 shutdown 函数,指定 SD_SEND 关闭 socket 的发送端。这允许客户端释放此 socket 的某些资源。服务器应用程序仍可以在该 socket 上接收数据。
// shutdown the send half of the connection since no more data will be sent
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{printf("shutdown failed: %d\n", WSAGetLastError());closesocket(ClientSocket);WSACleanup();return 1;
}
  1. 当客户端应用程序完成接收数据时,将调用 closesocket 函数关闭该 socket。

当客户端应用程序不再使用 Windows Sockets DLL,调用 WSACleanup 函数释放资源。

// cleanup
closesocket(ClientSocket);
WSACleanup();return 0;

4-A.7 Complete Winsock Server Code

以下是 Winsock TCP/IP 服务器应用程序的完整源代码。

#undef UNICODE#define WIN32_LEAN_AND_MEAN#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"int __cdecl main(void)
{WSADATA wsaData;int iResult;SOCKET ListenSocket = INVALID_SOCKET;SOCKET ClientSocket = INVALID_SOCKET;struct addrinfo *result = NULL;struct addrinfo hints;int iSendResult;char recvbuf[DEFAULT_BUFLEN];int recvbuflen = DEFAULT_BUFLEN;// Initialize WinsockiResult = WSAStartup(MAKEWORD(2,2), &wsaData);if (iResult != 0)  {printf("WSAStartup failed with error: %d\n", iResult);return 1;}ZeroMemory(&hints, sizeof(hints));hints.ai_family = AF_INET;hints.ai_socktype = SOCK_STREAM;hints.ai_protocol = IPPROTO_TCP;hints.ai_flags = AI_PASSIVE;// Resolve the server address and portiResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);if ( iResult != 0 )  {printf("getaddrinfo failed with error: %d\n", iResult);WSACleanup();return 1;}// Create a SOCKET for the server to listen for client connections.ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);if (ListenSocket == INVALID_SOCKET)  {printf("socket failed with error: %ld\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();return 1;}// Setup the TCP listening socketiResult = 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;}freeaddrinfo(result);iResult = listen(ListenSocket, SOMAXCONN);if (iResult == SOCKET_ERROR)  {printf("listen failed with error: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}// Accept a client socketClientSocket = accept(ListenSocket, NULL, NULL);if (ClientSocket == INVALID_SOCKET)  {printf("accept failed with error: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}// No longer need server socketclosesocket(ListenSocket);// Receive until the peer shuts down the connectiondo {iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);if (iResult > 0) {printf("Bytes received: %d\n", iResult);// Echo the buffer back to the senderiSendResult = send( ClientSocket, recvbuf, iResult, 0 );if (iSendResult == SOCKET_ERROR)  {printf("send failed with error: %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 with error: %d\n", WSAGetLastError());closesocket(ClientSocket);WSACleanup();return 1;}} while (iResult > 0);// shutdown the connection since we're doneiResult = shutdown(ClientSocket, SD_SEND);if (iResult == SOCKET_ERROR)  {printf("shutdown failed with error: %d\n", WSAGetLastError());closesocket(ClientSocket);WSACleanup();return 1;}// cleanupclosesocket(ClientSocket);WSACleanup();return 0;
}

4B. Winsock client application

4B.1 Creating a socket for the client

在初始化之后,必须实例化 SOCKET 对象以供客户端使用。

4B.1.1 To create a socket

  1. 声明一个 addrinfo 对象,包含一个 sockaddr 结构体并初始化这些值。对于这个应用,不指定 internet address family,因此 IPv6 或 IPv4 地址都可能被返回。协议为 TCP,socket type 为 stream socket。
struct addrinfo *result = NULL, *ptr = NULL, hints;ZeroMemory(&hints, sizeof(hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
  1. 调用 getaddrinfo 函数,在命令行上传递服务器名称的 IP 地址作为参数传给函数。服务器将 27015 作为默认端口用于客户端连接。getaddrinfo 函数返回一个整型并检查错误。
#define DEFAULT_PORT "27015"// Resolve the server address and port
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{printf("getaddrinfo failed: %d\n", iResult);WSACleanup();return 1;
}
  1. 创建一个 SOCKET 对象,名为 ConnectSocket。
SOCKET ConnectSocket = INVALID_SOCKET;
  1. 调用 socket 函数,将返回值保存到 ConnectSocket 变量。对于该示例,采用 getaddrinfo 函数返回的第一个与 hints 参数设置相匹配的 IP 地址。因为 address family 值为 AF_UNSPEC,表示未指定,因此返回 IP 地址可能是 IPv6 或 IPv4。

如果客户端应用程序希望仅使用 IPv6 或 IPv4,则 hints 参数的 address family 需要设置为 AF_INET6 或 AF_INET。并检查返回值,确保 socket 是有效的。

// Attempt to connect to the first address returned by
// the call to getaddrinfo
ptr=result;// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{printf("Error at socket(): %ld\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();return 1;
}

Note: hints.ai_family 设置为 AF_UNSPEC 可能会导致连接调用失败。如果发生了,则使用 IPv4(AF_INET) 或 IPv6(AF_INET6) 代替。

4B.2 Connecting to a Socket

客户端需要在网络中通信,它必须要连接到服务器。

4B.2.1 To connect to a socket

调用 connect 函数,将创建的 socket 和 sockaddr 结构体作为参数传递给函数。并检查错误。

// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{closesocket(ConnectSocket);ConnectSocket = INVALID_SOCKET;
}// Should really try the next address returned by getaddrinfo
// if the connect call failed
// But for this simple example we just free the resources
// returned by getaddrinfo and print an error messagefreeaddrinfo(result);if (ConnectSocket == INVALID_SOCKET)
{printf("Unable to connect to server!\n");WSACleanup();return 1;
}

Note: 如果 connect 函数调用失败,则尝试使用 addrinfo 结构体列表的下一个。

sockaddr 结构体包括:

  • 客户端想要连接的服务器的 IP 地址。

  • 客户端想要连接的服务器的端口号。

4B.3 Sending and Receiving Data on the Client

客户端一旦与服务器建立连接后,使用 send 和 recv 函数发送消息给服务器和接收来自服务器端的响应。

#define DEFAULT_BUFLEN 512int recvbuflen = DEFAULT_BUFLEN;const char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];int iResult;// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int) strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR)
{printf("send failed: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;
}printf("Bytes Sent: %ld\n", iResult);// shutdown the connection for sending since no more data will be sent
// the client can still use the ConnectSocket for receiving data
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{printf("shutdown failed: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;
}// Receive data until the server closes the connection
do {iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);if (iResult > 0)printf("Bytes received: %d\n", iResult);else if (iResult == 0)printf("Connection closed\n");elseprintf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);

4B.4 Disconnecting the Client

一旦客户端完成了发送和接收数据,客户端断开与服务器连接并关闭 socket。

4B.4.1 To disconnect and shutdown a socket

  1. 当客户端完成发送数据给服务器后,调用 shutdown 函数传入 SD_SEND 参数关闭客户端的发送功能。这将允许服务器释放这个 socket 的一些资源。客户端程序仍可以在该 socket 上接收数据。
// shutdown the send half of the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{printf("shutdown failed: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;
}
  1. 当客户端应用程序完成了接收数据,则调用 closesocket 函数关闭 socket。

当客户端应用程序完成了 Windows Sockets DLL 的调用,则调用 WSACleanup 函数释放资源。

// cleanup
closesocket(ConnectSocket);
WSACleanup();return 0;

4B.5 Complete Client Source Code

以下是 Winsock TCP/IP 客户端应用程序的完整源代码。

#define WIN32_LEAN_AND_MEAN#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#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;SOCKET ConnectSocket = INVALID_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;// Validate the parametersif (argc != 2)  {printf("usage: %s server-name\n", argv[0]);return 1;}// Initialize WinsockiResult = WSAStartup(MAKEWORD(2,2), &wsaData);if (iResult != 0)  {printf("WSAStartup failed with error: %d\n", iResult);return 1;}ZeroMemory( &hints, sizeof(hints) );hints.ai_family = AF_UNSPEC;hints.ai_socktype = SOCK_STREAM;hints.ai_protocol = IPPROTO_TCP;// Resolve the server address and portiResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);if ( iResult != 0 )  {printf("getaddrinfo failed with error: %d\n", iResult);WSACleanup();return 1;}// Attempt to connect to an address until one succeedsfor(ptr=result; ptr != NULL ;ptr=ptr->ai_next)  {// Create a SOCKET for connecting to serverConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);if (ConnectSocket == INVALID_SOCKET) {printf("socket failed with error: %ld\n", WSAGetLastError());WSACleanup();return 1;}// Connect to server.iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);if (iResult == SOCKET_ERROR)  {closesocket(ConnectSocket);ConnectSocket = INVALID_SOCKET;continue;}break;}freeaddrinfo(result);if (ConnectSocket == INVALID_SOCKET)  {printf("Unable to connect to server!\n");WSACleanup();return 1;}// Send an initial bufferiResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );if (iResult == SOCKET_ERROR)  {printf("send failed with error: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;}printf("Bytes Sent: %ld\n", iResult);// shutdown the connection since no more data will be sentiResult = shutdown(ConnectSocket, SD_SEND);if (iResult == SOCKET_ERROR)  {printf("shutdown failed with error: %d\n", WSAGetLastError());closesocket(ConnectSocket);WSACleanup();return 1;}// Receive until the peer closes the connectiondo {iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);if ( iResult > 0 )printf("Bytes received: %d\n", iResult);else if ( iResult == 0 )printf("Connection closed\n");elseprintf("recv failed with error: %d\n", WSAGetLastError());} while( iResult > 0 );// cleanupclosesocket(ConnectSocket);WSACleanup();return 0;
}

Windows 上的网络通信编程相关推荐

  1. android windows 上JNI编程

    昨天学习windows上的JNI编程,JNI说白了就是java和c语言的一个互相沟通的桥梁.java能够调用JNI来完毕调用C语言实现的方法. JNI的全称是(Java native interfac ...

  2. 手把手教你在Windows上编译Swift工具链

    2020/5/27更新:大家现在可以直接从Saleem Abdulrasool的Azure Pipeline中下载打包好的Swift Windows SDK了,在他的GitHub中找到swift-bu ...

  3. 运行时:Linux 和 Windows 2000上的高性能编程技术

    运行时:Linux 和 Windows 2000上的高性能编程技术 建立计时例程       级别: 初级 Edward G. Bradford, 高级程序员, IBM 2001 年 4 月 01 日 ...

  4. Windows上也能用Swift编程了,官方编译工具安装包现已上线

    萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 最近,Swift发行了5.3版本,这意味着它可以直接在Windows 10上使用了. 由于Swift是苹果开发的一门编程语言,能在Ubunt ...

  5. 通过Teardrop学习网络通信编程

    目录 一.实验相关概念 (一)原始套接字含义 (二)Teardrop攻击 二.步骤 1.配置VMware 2.编写Teardrop.c程序 3.编译运行 4.抓包分析 三.其它 实验目的:学习&quo ...

  6. 网络通信编程学习笔记(六):socket编程实战

    目录 一.wireshark抓包分析TCP协议 1.1.TCP协议的连接与断开简介 1.2.用wireshark抓取使用TCP协议传输的握手包 二.用Teardrop代码编写一个假包,并抓取 2.1. ...

  7. windows多线程和网络编程

    第 10 章  多线程与网络编程初步  教学提示:Windows 是一个支持多任务的操作系统.当在一个程序中需要启动另外一 个程序时,需要用到多进程的编程方式.如果一个进程中有一些相似的任务需要同时推 ...

  8. Windows环境下Unicode编程总结和将ANSI转换到Unicode 将Unicode转换到ANSI

    Windows环境下Unicode编程总结 UNICODE环境设置 在安装Visual Studio时,在选择VC++时需要加入unicode选项,保证相关的库文件可以拷贝到system32下. UN ...

  9. Windows客户端C/C++编程规范“建议”——风格

    9 风格 9.1 优先使用匈牙利命名法 等级: [推荐] 说明:该方法由微软总设计师设计.Windows上编程最好遵从该标准.详细介绍见:http://zh.wikipedia.org/wiki/%E ...

最新文章

  1. 自定义表单mysql_自定义表单,计算答案然后更新mysql DB(Custom form, calculate answer then update mysql DB)...
  2. BTC跟跌黄金,BCH领涨主流 2019.8.14
  3. 2015 年,我和华大基因立下一个小目标……
  4. python getattr函数_[转]Python中的getattr()函数详解
  5. linux搭建Nexus初次体验
  6. oracle 数据立方_大数据之数据仓库分层
  7. MyBatis框架——mybatis插入数据返回主键(mysql、oracle)
  8. Sping boot系列--redis之2 -- RedisKeyValueTemplate处理Model对象
  9. http请求代理proxy-ajax
  10. SQL 基础之单行函数(七)
  11. 使用一个DataContext,还是多个?
  12. SpringBoot+Quartz实现动态可配定时任务(动态定时任务)
  13. 12款开源或免费的3D建模软件
  14. jar命令 解压/压缩 war、jar文件
  15. 第1期丨每周软件网站白嫖
  16. 云安全技术——PGP加密技术
  17. kafka集群搭建(三台服务器)
  18. Vue3格式化Volar报错
  19. mysql mha etcd_postgresql 高可用 etcd + patroni 之四 failover
  20. html把保留图片改为提交按钮,如何制作图片按钮,并为图片按钮添加提交表单和重置表单功能...

热门文章

  1. json mysql 字段 默认值_MySQL新增JSON类型字段的使用总结
  2. 实时渲染大赛太卷了,来看提前交卷的优秀参赛作品
  3. Unity事件分发器
  4. 动态测试与静态测试--手工测试和自动化测试
  5. Java枚举类型介绍
  6. IT学习的网站和工具
  7. 抖音微商引流之抖音实战引流技巧,,抖音/快手/火山热门技术---
  8. docker golang buid rstp2webrtc
  9. 青云服务器换系统,青云科技发布新一代企业级云服务器e3:算力供给侧变革的强大助手...
  10. 怎样由标准正态分布得到任意正态分布