Windows 上的网络通信编程
1. 关于 Servers 和 Clients
有两种不同类型的套接字网络应用程序:服务器和客户端。
服务器和客户端的行为不同;因此,创建它们的流程也是不一样的。以下是用于创建流式 TCP/IP 服务器和客户端的一般模型。
1.1 Server
Initialize Winsock.
Create a socket.
Bind the socket.
Listen on the socket for a client.
Accept a connection from a client.
Receive and send data.
Disconnect.
1.2 Client
Initialize Winsock.
Create a socket.
Connect to the server.
Send and receive data.
Disconnect.
2. Create a Basic Winsock Application
创建一个基本的 Winsock 应用需要基于以下条件:
确保编译环境可以引用 SDK 的 include,Lib 和 Src 目录。
确保编译环境可以链接到 Winsock 库文件(Ws2_32.lib)。可以通过 #pragma comment(lib, “ws2_32.lib”) 显式的链接。
编程开始前,使用 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
- 创建一个 WSADATA 对象,名为 wsaData。
WSADATA wsaData;
- 调用 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
- [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;
}
- 创建一个 SOCKET 对象,名为 ListenSocket,用于服务器监听客户端的连接。
SOCKET ListenSocket = INVALID_SOCKET;
- 调用 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);
- 检查 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
- 创建一个临时的 SOCKET 对象,名为 ClientSocket,用于接收来自客户端的连接请求。
SOCKET clientSocket;
- 通常,服务器应用程序将设计为监听来自多个客户端的连接。对于高性能服务器,通常使用多个线程来处理多个客户端连接。
使用 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;
}
- 当接收到客户端连接,服务器应用程序通常会将接受的客户端 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
- 当服务器完成向客户端发送数据时,可以调用 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;
}
- 当客户端应用程序完成接收数据时,将调用 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
- 声明一个 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;
- 调用 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;
}
- 创建一个 SOCKET 对象,名为 ConnectSocket。
SOCKET ConnectSocket = INVALID_SOCKET;
- 调用 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
- 当客户端完成发送数据给服务器后,调用 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;
}
- 当客户端应用程序完成了接收数据,则调用 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 上的网络通信编程相关推荐
- android windows 上JNI编程
昨天学习windows上的JNI编程,JNI说白了就是java和c语言的一个互相沟通的桥梁.java能够调用JNI来完毕调用C语言实现的方法. JNI的全称是(Java native interfac ...
- 手把手教你在Windows上编译Swift工具链
2020/5/27更新:大家现在可以直接从Saleem Abdulrasool的Azure Pipeline中下载打包好的Swift Windows SDK了,在他的GitHub中找到swift-bu ...
- 运行时:Linux 和 Windows 2000上的高性能编程技术
运行时:Linux 和 Windows 2000上的高性能编程技术 建立计时例程 级别: 初级 Edward G. Bradford, 高级程序员, IBM 2001 年 4 月 01 日 ...
- Windows上也能用Swift编程了,官方编译工具安装包现已上线
萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 最近,Swift发行了5.3版本,这意味着它可以直接在Windows 10上使用了. 由于Swift是苹果开发的一门编程语言,能在Ubunt ...
- 通过Teardrop学习网络通信编程
目录 一.实验相关概念 (一)原始套接字含义 (二)Teardrop攻击 二.步骤 1.配置VMware 2.编写Teardrop.c程序 3.编译运行 4.抓包分析 三.其它 实验目的:学习&quo ...
- 网络通信编程学习笔记(六):socket编程实战
目录 一.wireshark抓包分析TCP协议 1.1.TCP协议的连接与断开简介 1.2.用wireshark抓取使用TCP协议传输的握手包 二.用Teardrop代码编写一个假包,并抓取 2.1. ...
- windows多线程和网络编程
第 10 章 多线程与网络编程初步 教学提示:Windows 是一个支持多任务的操作系统.当在一个程序中需要启动另外一 个程序时,需要用到多进程的编程方式.如果一个进程中有一些相似的任务需要同时推 ...
- Windows环境下Unicode编程总结和将ANSI转换到Unicode 将Unicode转换到ANSI
Windows环境下Unicode编程总结 UNICODE环境设置 在安装Visual Studio时,在选择VC++时需要加入unicode选项,保证相关的库文件可以拷贝到system32下. UN ...
- Windows客户端C/C++编程规范“建议”——风格
9 风格 9.1 优先使用匈牙利命名法 等级: [推荐] 说明:该方法由微软总设计师设计.Windows上编程最好遵从该标准.详细介绍见:http://zh.wikipedia.org/wiki/%E ...
最新文章
- 自定义表单mysql_自定义表单,计算答案然后更新mysql DB(Custom form, calculate answer then update mysql DB)...
- BTC跟跌黄金,BCH领涨主流 2019.8.14
- 2015 年,我和华大基因立下一个小目标……
- python getattr函数_[转]Python中的getattr()函数详解
- linux搭建Nexus初次体验
- oracle 数据立方_大数据之数据仓库分层
- MyBatis框架——mybatis插入数据返回主键(mysql、oracle)
- Sping boot系列--redis之2 -- RedisKeyValueTemplate处理Model对象
- http请求代理proxy-ajax
- SQL 基础之单行函数(七)
- 使用一个DataContext,还是多个?
- SpringBoot+Quartz实现动态可配定时任务(动态定时任务)
- 12款开源或免费的3D建模软件
- jar命令 解压/压缩 war、jar文件
- 第1期丨每周软件网站白嫖
- 云安全技术——PGP加密技术
- kafka集群搭建(三台服务器)
- Vue3格式化Volar报错
- mysql mha etcd_postgresql 高可用 etcd + patroni 之四 failover
- html把保留图片改为提交按钮,如何制作图片按钮,并为图片按钮添加提交表单和重置表单功能...