Windows 上的网络通信编程

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 
#include 

#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 
#include 
#include 
#include 

#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 512

char 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 sender
        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 函数都分别返回发送或接收的字节数的整数值,或返回错误。每个函数还采用相同的参数: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 
#include 
#include 
#include 
#include 

// 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 Winsock
    iResult = 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 port
    iResult = 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 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;
    }

    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 socket
    ClientSocket = 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 socket
    closesocket(ListenSocket);

    // 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 sender
            iSendResult = 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 done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR)  
    {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    // cleanup
    closesocket(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 message

freeaddrinfo(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 512

int 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");
    else
        printf("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 
#include 
#include 
#include 
#include 


// 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 parameters
    if (argc != 2)  
    {
        printf("usage: %s server-name\n", argv[0]);
        return 1;
    }

    // Initialize Winsock
    iResult = 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 port
    iResult = 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 succeeds
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)  
    {

        // Create a SOCKET for connecting to server
        ConnectSocket = 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 buffer
    iResult = 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 sent
    iResult = 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 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");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());

    } while( iResult > 0 );

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

    return 0;
}

你可能感兴趣的:(网络编程,windows,服务器,网络,socket,websocket)