前置只是参考winsocket-cli

创建

    WSADATA wsaData;
    int iResult;
    //initialize Winsock
    iResult= WSAStartup(MAKEWORD(2,2),&wsaData);
    if(iResult!=0){
        std::cout<<"init failed: "<<iResult;
        return 1;
    }
    std::cout<<"start success\n";
    std::string defaultPort{"27015"};
    struct addrinfo
            *result= nullptr,
            *ptr= nullptr,
            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(nullptr,defaultPort.c_str(),&hints,&result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET listenSocket=INVALID_SOCKET;
    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;
    }
    std::cout<<"creat success\n";

特别:AI_PASSIVE 标志指示调用方打算在 调用绑定 函数时使用返回的套接字地址结构。 如果设置了 AI_PASSIVE 标志,并且 getaddrinfo 函数的 nodename 参数为 NULL 指针,则套接字地址结构的 IP 地址部分将设置为 IPv4 地址的 INADDR_ANY,或 iPv6 地址的 IN6ADDR_ANY_INIT

调用 套接字 函数并将其值返回到 ListenSocket 变量。 对于此服务器应用程序,请使用调用 getaddrinfo 返回的第一个 IP 地址,该地址与 hints 参数中指定的地址系列、套接字类型和协议匹配。 在此示例中,请求了 IPv4 的 TCP 流套接字,地址系列为 IPv4,套接字类型为 SOCK_STREAM,协议为 IPPROTO_TCP。 因此,为 ListenSocket 请求 IPv4 地址。

如果服务器应用程序想要侦听 IPv6,则需要在 hints 参数中将地址系列设置为 AF_INET6。 如果服务器想要同时侦听 IPv6 和 IPv4,则必须创建两个侦听套接字,一个用于 IPv6,另一个用于 IPv4。 应用程序必须单独处理这两个套接字。

绑定

iResult= bind(listenSocket,result->ai_addr,static_cast<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);
std::cout<<"bind success\n";

bind:函数将本地地址与套接字相关联

监听

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

调用 listen 函数,将作为参数传递给创建的套接字和 积压工作的值,以及要接受的挂起连接队列的最大长度。 在此示例中, 积压工作 参数设置为 SOMAXCONN。 此值是一个特殊常量,指示此套接字的 Winsock 提供程序允许队列中最大合理的挂起连接数。 检查一般错误的返回值。

接受

SOCKET cliSocket=INVALID_SOCKET;
cliSocket= accept(listenSocket, nullptr, nullptr);
if (cliSocket == INVALID_SOCKET) {
    printf("accept failed: %d\n", WSAGetLastError());
    closesocket(listenSocket);
    WSACleanup();
    return 1;
}
std::cout<<"accept success\n";

使用 Winsock 有多种不同的编程技术可用于侦听多个客户端连接。 一种编程技术是创建一个连续循环,该循环使用 侦听 函数检查连接请求 (请参阅 侦听套接字) 。 如果发生连接请求,应用程序将调用 acceptAcceptExWSAAccept 函数,并将工作传递给另一个线程来处理请求。 还可以使用其他几种编程技术。请注意,此基本示例非常简单,不使用多个线程。 该示例还只侦听 并仅接受单个连接。

accept:允许在套接字上进行传入连接尝试。

两个null:

[out] addr

指向接收连接实体地址的缓冲区的可选指针,该地址称为通信层。 addr 参数的确切格式由创建 sockaddr 结构中的套接字时建立的地址系列确定。

[in, out] addrlen

指向包含 addr 参数指向的结构长度的整数的可选指针。

收发信息与结束

//no longer need listen socket
closesocket(listenSocket);
int iSendResult=0;
const int receiveBuffLen=512;
char receiveBuff[receiveBuffLen];
do{
    std::cout<<"receiveing\n";
    iResult= recv(cliSocket,receiveBuff,receiveBuffLen,0);
    if (iResult>0){
        printf("Bytes received: %d\n",iResult);
        std::cout<<"buff is: "<<receiveBuff<<'\n';
        // Echo the buffer back to the sender
        iSendResult = send( cliSocket, receiveBuff, iResult, 0 );
        if (iSendResult == SOCKET_ERROR) {
            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(cliSocket);
            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(cliSocket);
        WSACleanup();
        return 1;
    }
} while (iResult>0);
// shutdown the connection since we're done
iResult = shutdown(cliSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
    printf("shutdown failed with error: %d\n", WSAGetLastError());
    closesocket(cliSocket);
    WSACleanup();
    return 1;
}
// cleanup
closesocket(cliSocket);
WSACleanup();
std::cout<<"closed\n";

完整代码:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
int main() {
    WSADATA wsaData;
    int iResult;
    //initialize Winsock
    iResult= WSAStartup(MAKEWORD(2,2),&wsaData);
    if(iResult!=0){
        std::cout<<"init failed: "<<iResult;
        return 1;
    }
    std::cout<<"start success\n";
    std::string defaultPort{"27015"};
    struct addrinfo
            *result= nullptr,
            *ptr= nullptr,
            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(nullptr,defaultPort.c_str(),&hints,&result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET listenSocket=INVALID_SOCKET;
    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;
    }
    std::cout<<"creat success\n";
    iResult= bind(listenSocket,result->ai_addr,static_cast<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);
    std::cout<<"bind success\n";
    if ( listen( listenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
        printf( "Listen failed with error: %ld\n", WSAGetLastError() );
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }
    std::cout<<"listen success\n";
    SOCKET cliSocket=INVALID_SOCKET;
    cliSocket= accept(listenSocket, nullptr, nullptr);
    if (cliSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }
    std::cout<<"accept success\n";
    //no longer need listen socket
    closesocket(listenSocket);
    int iSendResult=0;
    const int receiveBuffLen=512;
    char receiveBuff[receiveBuffLen];
    do{
        std::cout<<"receiveing\n";
        iResult= recv(cliSocket,receiveBuff,receiveBuffLen,0);
        if (iResult>0){
            printf("Bytes received: %d\n",iResult);
            std::cout<<"buff is: "<<receiveBuff<<'\n';
            // Echo the buffer back to the sender
            iSendResult = send( cliSocket, receiveBuff, iResult, 0 );
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(cliSocket);
                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(cliSocket);
            WSACleanup();
            return 1;
        }
    } while (iResult>0);
    // shutdown the connection since we're done
    iResult = shutdown(cliSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(cliSocket);
        WSACleanup();
        return 1;
    }
    // cleanup
    closesocket(cliSocket);
    WSACleanup();
    std::cout<<"closed\n";
    return 0;
}