microsoft Documents

初始环境:cmake stl23

环境配置:

add_library(WS2 SHARED IMPORTED)
set_target_properties(WS2 PROPERTIES
        IMPORTED_IMPLIB "C:\\Windows\\System32\\ws2_32.dll"
)
target_link_libraries(winsocket WS2)
file(COPY "C:\\Windows\\System32\\ws2_32.dll" DESTINATION ${CMAKE_BINARY_DIR})
//模板
add_library(库名 SHARED IMPORTED)
set_target_properties(库名 PROPERTIES
	IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/external/DLL文件名.dll
	IMPORTED_IMPLIB ${CMAKE_SOURCE_DIR}/external/LIB文件名.lib
)
target_link_libraries(程序名 库名)

初始化:

WSADATA wsaData;
int iResult;
//initialize Winsock
iResult= WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult!=0){
    std::cout<<"init failed: "<<iResult;
    return 1;
}

WSADATA:包含有关 Windows 套接字实现的信息

WSAStartup:通过进程启动对 Winsock DLL 的使用,第一个参数是unsigned short的值,但是低位和高位指定版本,MAKEWORD辅助创建

[out] lpWSAData:指向 WSADATA 数据结构的指针,该结构用于接收 Windows 套接字实现的详细信息。

失败返回-1

客户端:

创建套接字

1.声明包含 sockaddr 结构的 addrinfo 对象并初始化这些值。 对于此应用程序,未指定 Internet 地址系列,因此可以返回 IPv6 或 IPv4 地址。 应用程序将套接字类型请求为 TCP 协议的流套接字。

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;

socketaddr:关于地址信息的结构体

addrinfo:getaddrinfo 函数使用 addrinfo 结构来保存主机地址信息

ai_family:当前支持的值 是 AF_INETAF_INET6,它们是 IPv4 和 IPv6 的 Internet 地址系列格式。

ai_socktype:

含义

SOCK_STREAM1

提供具有 OOB 数据传输机制的有序、可靠、双向、基于连接的字节流。 将传输控制协议 (TCP) 用于 Internet 地址系列 (AF_INETAF_INET6) 。 如果ai_family成员AF_IRDA,SOCK_STREAM是唯一受支持的套接字类型。

SOCK_DGRAM2

支持数据报,即最大长度固定(通常很小)的无连接、不可靠缓冲区。 将用户数据报协议 (UDP) 用于 Internet 地址系列 (AF_INETAF_INET6) 。

SOCK_RAW3

提供允许应用程序操作下一层协议标头的原始套接字。 若要操作 IPv4 标头,必须在套接字上设置 IP_HDRINCL 套接字选项。 若要操作 IPv6 标头,必须在套接字上设置 IPV6_HDRINCL 套接字选项。

SOCK_RDM4

提供可靠的消息数据报。 此类型的一个示例是 Windows 中的实用常规多播 (PGM) 多播协议实现,通常称为 可靠多播编程

SOCK_SEQPACKET5

提供基于数据报的伪流数据包。

ai_portocal:协议类型:

含义

IPPROTO_TCP6

传输控制协议 (TCP) 。 当ai_family成员AF_INET或AF_INET6ai_socktype成员SOCK_STREAM时,此值是可能的。

IPPROTO_UDP17

用户数据报协议 (UDP) 。 当ai_family成员AF_INET或AF_INET6类型参数SOCK_DGRAM时,此值可能为

IPPROTO_RM113

可靠多播的 PGM 协议。 当ai_family成员AF_INET且ai_socktype成员为SOCK_RDM时,此值是可能的。 在 Windows Vista 及更高版本发布的Windows SDK中,此值也称为IPPROTO_PGM

    std::string defaultPort="27015";
    std::string nodeName="127.0.0.1";
    // Resolve the server address and port
    iResult= getaddrinfo(nodeName.c_str(),defaultPort.c_str(),&hints,&result);
    if (iResult!=0){
        std::cout<<"getaddrinfo failed: "<<iResult;
        WSACleanup();
        return 1;
    }

getaddrinfo:addrinfo 结构来保存主机地址信息,result是一个结构体链表

SOCKET ConnectSocket =INVALID_SOCKET;
ptr=result;
ConnectSocket= socket(ptr->ai_family,ptr->ai_socktype,ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
    std::cout<<"socket error: "<<WSAGetLastError();
    freeaddrinfo(result);
    WSACleanup();
    return 1;
}

SOCKET:套接字的描述符

socket():函数创建绑定到特定传输服务提供程序的套接字

freeaddrinfo:函数释放 getaddrinfo 函数在 addrinfo 结构中动态分配的地址信息。

WSACleanup:函数终止使用 Winsock 2 DLL (Ws2_32.dll)

总结:到目前为止,只从系统获取了socket相关信息。
连接到套接字

    // Connect to server.
    iResult= connect(ConnectSocket,ptr->ai_addr,static_cast<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){
        std::cout<<"ubanle to connect Server\n";
        WSACleanup();
        return 1;
    }

connect:与指定的套接字建立连接;

freeaddrinfo:释放内存;

tcp三次握手在此完成。

发送&接受

    // Send an initial buffer
    const int receiveBufLen=512;
    char receiveBBuf[receiveBufLen];
    std::string sendBuf{"this is a text"};
    iResult= send(ConnectSocket,sendBuf.c_str(),static_cast<int>(sendBuf.length()),0);
    if (iResult==SOCKET_ERROR){
        std::cout<<"send error "<<WSAGetLastError();
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    std::cout<<"send success: "<<iResult<<'\n';
// 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, receiveBBuf, receiveBufLen, 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);
    std::cout<<"receive success\n";

send:函数在连接的套接字上发送数据

recv:从连接的套接字或绑定的无连接套接字接收数据。

shutdown:

如果how参数为 SD_RECEIVE, 则将不允许对套接字上的recv函数进行后续调用。这对较低的协议层没有影响。对于 TCP 套接字,如果套接字上仍有数据排队等待接收,或者随后有数据到达,则连接将重置,因为无法将数据传送给用户。对于 UDP 套接字,将接受传入的数据报并将其排队。在任何情况下都不会生成 ICMP 错误数据包。

如果how参数为 SD_SEND,则不允许后续调用 send函数。对于 TCP 套接字,所有数据发送完毕并由接收方确认后,将发送 FIN。

设置如何SD_BOTH 将禁用发送和接收,如上所述。

关闭

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

完整代码:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>

int main(int argc,char **argv) {
    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";
    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;
    std::cout<<"hints success\n";
    std::string defaultPort="27015";
    std::string nodeName="127.0.0.1";
    // Resolve the server address and port
    iResult= getaddrinfo(nodeName.c_str(),defaultPort.c_str(),&hints,&result);
    if (iResult!=0){
        std::cout<<"getaddrinfo failed: "<<iResult;
        WSACleanup();
        return 1;
    }
    std::cout<<"get success\n";
    SOCKET ConnectSocket =INVALID_SOCKET;
    for(ptr=result;ptr!= nullptr;ptr=ptr->ai_next){
        ConnectSocket= socket(ptr->ai_family,ptr->ai_socktype,ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            std::cout<<"socket error: "<<WSAGetLastError();
            freeaddrinfo(result);
            WSACleanup();
            return 1;
        }
        std::cout<<"create success\n";
        // Connect to server.
        iResult= connect(ConnectSocket,ptr->ai_addr,static_cast<int>(ptr->ai_addrlen));
        if (iResult==SOCKET_ERROR){
            closesocket(ConnectSocket);
            ConnectSocket=INVALID_SOCKET;
            continue;
        }
        break;
    }
    // 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){
        std::cout<<"ubanle to connect Server\n";
        WSACleanup();
        return 1;
    }
    std::cout<<"connect success\n";
    // Send an initial buffer
    const int receiveBufLen=512;
    char receiveBBuf[receiveBufLen];
    std::string sendBuf{"this is a text"};
    iResult= send(ConnectSocket,sendBuf.c_str(),static_cast<int>(sendBuf.length()),0);
    if (iResult==SOCKET_ERROR){
        std::cout<<"send error "<<WSAGetLastError();
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    std::cout<<"send success: "<<iResult<<'\n';
// 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, receiveBBuf, receiveBufLen, 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);
    std::cout<<"receive success\n";
    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();
    return 0;
}