美文网首页
C++Socket套接字编程使用winsock2.h

C++Socket套接字编程使用winsock2.h

作者: 再改一个名 | 来源:发表于2022-02-24 22:23 被阅读0次

套接字有两种概念:服务器和客户端
下面是用于创建流式 (steam)TCP/IP 服务器和客户端的常规模型。

服务器

  1. 初始化 Winsock。
  2. 创建套接字。
  3. 绑定套接字。
  4. 在套接字上监听客户端。
  5. 接受来自客户端的连接。
  6. 接收和发送数据。
  7. 断开连接

客户端

  1. 初始化 Winsock。
  2. 创建套接字。
  3. 连接到该服务器。
  4. 发送和接收数据。
  5. 断开连接

很明显, 1, 2, 还有 断开连接 步骤完全相同

0.检测环境

用vs, 调试下面代码, 判断环境是否准备充分

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

int main() {
    printf("Hello World");
    return 0;
}

1. 初始化

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

int main() {
    WSADATA wsaData;
    int iResult;

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }

    printf("Hello World");
    return 0;
}

2. 服务器端创建套接字

首先为服务器创建套接字, 这样接下来的客户端就可以连接调试

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")


#define DEFAULT_PORT "9501" // 服务器监听的端口

int main() {
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
    struct addrinfo* 
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建socket对象
    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;
    }
    // 2. 创建套接字 end


    return 0;
}

每个步骤尽量都要调试一番, 已检查是否错误

3.绑定套接字

若要使服务器接受客户端连接,它必须绑定到服务器的网络地址。

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")


#define DEFAULT_PORT "9501" // 服务器监听的端口

int main() {
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
    struct addrinfo* 
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建socket对象
    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;
    }
    // 2. 创建套接字 end

    // 3. 绑定套接字 start
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);// 调用 bind 函数后,不再需要地址信息 释放
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 3. 绑定套接字 end

    return 0;
}

4.在套接字上监听客户端

将套接字绑定到系统上的 IP 地址和端口之后,服务器必须在该 IP 地址和端口上侦听传入的连接请求

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")


#define DEFAULT_PORT "9501" // 服务器监听的端口

int main() {
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
    struct addrinfo* 
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建socket对象
    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;
    }
    // 2. 创建套接字 end

    // 3. 绑定套接字 start
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);// 调用 bind 函数后,不再需要地址信息 释放
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 3. 绑定套接字 end

    // 4.在套接字上监听客户端 start
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
        // SOMAXCONN定义了此套接字允许最大连接
        printf("Listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 4.在套接字上监听客户端 end

    return 0;
}

备注: window10第一次调试这一步骤会让用户给予防火墙权限

5. 接受来自客户端的连接。

当套接字侦听连接后,程序必须处理该套接字上的连接请求

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")


#define DEFAULT_PORT "9501" // 服务器监听的端口

int main() {
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
    struct addrinfo* 
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建socket对象
    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;
    }
    // 2. 创建套接字 end

    // 3. 绑定套接字 start
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);// 调用 bind 函数后,不再需要地址信息 释放
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 3. 绑定套接字 end

    // 4.在套接字上监听客户端 start
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
        // SOMAXCONN定义了此套接字允许最大连接
        printf("Listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 4.在套接字上监听客户端 end
    
    // 5.接受来自客户端的连接 start
    SOCKET ClientSocket; //临时 套接字 对象,以接受来自客户端的连接
    // 这里没有考虑多线程并发接受多个客户端 只接受一个
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 5.接受来自客户端的连接 end
    return 0;
}

备注:运行这一步时, 控制台似乎没有显示任何东西, 其实 是accept 将逻辑流程卡住 等待 客户端连接, 如下图所示


accept 将逻辑流程卡住

6.在服务器上接收和发送数据

服务器接收的数据来自客户端, 发送也是向客户端发送数据, 故而需要等下面的客户端socket编写完毕才能进行最终的功能测试.

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")




int main() {
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
#define DEFAULT_PORT "9501" // 服务器监听的端口
    struct addrinfo* 
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建socket对象
    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;
    }
    // 2. 创建套接字 end

    // 3. 绑定套接字 start
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);// 调用 bind 函数后,不再需要地址信息 释放
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 3. 绑定套接字 end

    // 4.在套接字上监听客户端 start
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
        // SOMAXCONN定义了此套接字允许最大连接
        printf("Listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 4.在套接字上监听客户端 end
    
    // 5.接受来自客户端的连接 start
    SOCKET ClientSocket; //临时 套接字 对象,以接受来自客户端的连接
    // 这里没有考虑多线程并发接受多个客户端 只接受一个
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 5.接受来自客户端的连接 end

    // 6.在服务器上接收和发送数据 start
#define DEFAULT_BUFLEN 512 //  字符缓冲区长度
    char recvbuf[DEFAULT_BUFLEN]; // 字符缓冲区数组
    int  iSendResult;
    int recvbuflen = DEFAULT_BUFLEN;

    do {
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            // recvbuf参数表示: 这里为了简单将客户端发送过来的消息再发送给客户端 
            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);
    // 6.在服务器上接收和发送数据 end

    return 0;
}

备注: 这一步相当于完成了服务器的书写,但为了保险, 还是要关闭连接

7.断开连接

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")




int main() {
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
#define DEFAULT_PORT "9501" // 服务器监听的端口
    struct addrinfo* 
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // 创建socket对象
    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;
    }
    // 2. 创建套接字 end

    // 3. 绑定套接字 start
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);// 调用 bind 函数后,不再需要地址信息 释放
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 3. 绑定套接字 end

    // 4.在套接字上监听客户端 start
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
        // SOMAXCONN定义了此套接字允许最大连接
        printf("Listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 4.在套接字上监听客户端 end
    
    // 5.接受来自客户端的连接 start
    SOCKET ClientSocket; //临时 套接字 对象,以接受来自客户端的连接
    // 这里没有考虑多线程并发接受多个客户端 只接受一个
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 5.接受来自客户端的连接 end

    // 6.在服务器上接收和发送数据 start
#define DEFAULT_BUFLEN 512 //  字符缓冲区长度
    char recvbuf[DEFAULT_BUFLEN]; // 字符缓冲区数组
    int  iSendResult;
    int recvbuflen = DEFAULT_BUFLEN;

    do {
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            // recvbuf参数表示: 这里为了简单将客户端发送过来的消息再发送给客户端 
            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);
    // 6.在服务器上接收和发送数据 end

    // 7.断开连接 start
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }
    // 7.断开连接 end

    return 0;
}

备注: 这里没有写控制台输入判断 进行关闭服务器 而是等客户端传输完数据后自动执行关闭逻辑

1.初始化

这一步和服务端一样, 见最上面

2.客户端创建套接字

需要重新用vs创建一个新项目当做客户端

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")




int main() {
    printf("启动客户端");
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
#define DEFAULT_IP "127.0.0.1"// 服务器为本机
#define DEFAULT_PORT "9501" // 服务器监听的端口
    struct addrinfo*
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET ConnectSocket  = INVALID_SOCKET;
    ConnectSocket  = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ConnectSocket  == INVALID_SOCKET) {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }
    // 2. 创建套接字 end

    

    return 0;
}

3.客户端连接到该服务器

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")




int main() {
    printf("启动客户端");
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
#define DEFAULT_IP "127.0.0.1"// 服务器为本机
#define DEFAULT_PORT "9501" // 服务器监听的端口
    struct addrinfo*
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET ConnectSocket  = INVALID_SOCKET;
    ConnectSocket  = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ConnectSocket  == INVALID_SOCKET) {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }
    // 2. 创建套接字 end

    // 3. 连接到套接字 start
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        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;
        }


        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }
    // 3. 连接到套接字 end
    freeaddrinfo(result);

    return 0;
}

4.客户端发送和接收数据

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")




int main() {
    printf("启动客户端");
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
#define DEFAULT_IP "127.0.0.1"// 服务器为本机
#define DEFAULT_PORT "9501" // 服务器监听的端口
    struct addrinfo*
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET ConnectSocket  = INVALID_SOCKET;
    ConnectSocket  = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ConnectSocket  == INVALID_SOCKET) {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }
    // 2. 创建套接字 end

    // 3. 连接到套接字 start
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        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;
        }


        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }
    // 3. 连接到套接字 end
    freeaddrinfo(result);

    // 4.在客户端上发送和接收数据 start
#define DEFAULT_BUFLEN 512

    int recvbuflen = DEFAULT_BUFLEN;

    const char* sendbuf = "Hello World";
    char recvbuf[DEFAULT_BUFLEN];
    // 发送
    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);

    // 关闭
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // 接收
    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);
    // 4.在客户端上发送和接收数据 end



    return 0;
}

5.客户端断开连接

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")




int main() {
    printf("启动客户端");
    // 1. 初始化 start
    WSADATA wsaData;
    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    // 1. 初始化 end

    // 2. 创建套接字 start
#define DEFAULT_IP "127.0.0.1"// 服务器为本机
#define DEFAULT_PORT "9501" // 服务器监听的端口
    struct addrinfo*
        result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
    hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
    hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 协议
    hints.ai_flags = AI_PASSIVE;

    // 从本机中获取ip地址等信息为了sockcet 使用
    iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET ConnectSocket  = INVALID_SOCKET;
    ConnectSocket  = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ConnectSocket  == INVALID_SOCKET) {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }
    // 2. 创建套接字 end

    // 3. 连接到套接字 start
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        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;
        }


        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }
    // 3. 连接到套接字 end
    freeaddrinfo(result);

    // 4.在客户端上发送和接收数据 start
#define DEFAULT_BUFLEN 512

    int recvbuflen = DEFAULT_BUFLEN;

    const char* sendbuf = "Hello World";
    char recvbuf[DEFAULT_BUFLEN];
    // 发送
    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);

    // 关闭
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // 接收
    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);
    // 4.在客户端上发送和接收数据 end

    // 5.断开连接 start
    // 这里和服务器断开连接写在最后不同, 客户端断开连接写在 发送后 和 接收前
    // shutdown(ConnectSocket, SD_SEND) SD_SEND表示socket的发送数据端虽然关闭(为了服务器释放客户端连接资源), 但是仍然能接收服务端的数据
    //iResult = shutdown(ClientSocket, SD_SEND);
    //if (iResult == SOCKET_ERROR) {
    //  printf("shutdown failed: %d\n", WSAGetLastError());
    //  closesocket(ClientSocket);
    //  WSACleanup();
    //  return 1;
    //}
    closesocket(ConnectSocket);
    WSACleanup();
    // 5.断开连接 end

    return 0;
}

结果

启动服务端 和客户端 可以看到, 双方都能接收到


image.png

备注: 本文内容来源于 [微软官方文档] (https://docs.microsoft.com/zh-cn/windows/win32/winsock/using-winsock)
因为文档 内容繁多 ,特做笔记

相关文章

  • C++Socket套接字编程使用winsock2.h

    套接字有两种概念:服务器和客户端下面是用于创建流式 (steam)TCP/IP 服务器和客户端的常规模型。 服务器...

  • 网络编程基础(一)

    网络编程基础(一) 一、套接字 1.分类1.1面向连接的套接字 1.2面向无连接的套接字 二、基本使用 1.soc...

  • 2018-09-12 Day18 网络基础

    01.socket编程 socket又叫套接字,就是进行数据通信两端。分为服务端套接字和客户端套接字。套接字编程:...

  • 2018-09-12 day18-网络基础

    总结 一.socket编程 socket又叫套接字,就是进行数据通信两端,分为服务端套接字和客户端套接字套接字编程...

  • day18-网络基础总结

    一、scoket编程: socket又叫套接字,就是进行数据通信两端。分为服务端套接字和客户端套接字套接字编程:自...

  • 18总 网络基础

    1.socket编程: socket又叫套接字,就是进行数据通信两端。分为服务端套接字和客户端套接字套接字编程:自...

  • 2018-09-12 day18网络基础总结

    一socket编程 socket又叫套接字,就是进行数据通信两端。分为服务端套接字和客户端套接字套接字编程:自己写...

  • 2018-09-12-总结

    一、socket编程 socket又叫套接字,就是进行数据通信的两端。分为服务端套接字和客户端套接字套接字编程:自...

  • socket 和 网络I/O模型

    《UNIX 网络编程卷一:套接字联网API》笔记 套接字 套接字编程接口,是在 TCP/IP 协议族中,应用层进入...

  • 2018-09-12 day18网络编程

    1.socket套接字 socket又叫套接字,就是进行数据通信两端。分为服务端套接字和客户端套接字 套接字编程:...

网友评论

      本文标题:C++Socket套接字编程使用winsock2.h

      本文链接:https://www.haomeiwen.com/subject/abhtrrtx.html