• Windows网络通信(一):socket同步编程


    网络通信常用API

    1. WSAStartup用于初始化WinSock环境

    int WSAStartup(
      WORD      wVersionRequested,
      LPWSADATA lpWSAData
    );

    wVersionRequested:当前进程能够使用Windows Socket的最高版本,目前指定2.2即可。

    lpWSAData:指向一个WSAData结构体,接受Socket详细信息。

    成功返回0

    2. socket建立一个指定类型的SOCKET用于通信

    SOCKET WSAAPI socket(
      int af,
      int type,
      int protocol
    );

    af:address family,指定用于通信的网络地址类型,可以取值AF_INET(IPv4),AF_INET6(IPv6),AF_BTH(蓝牙)等。

    type:指定传输类型,可以取值SOCK_STREAM(用于TCP),SOCK_DGRAM(用于UDP)等。

    protocol:通信协议,可以取值IPPROTO_TCP,IPPROTO_UDP等。

    成功返回一个可以用于通信的SOCKET,否则返回INVALID_SOCKET。

    3. bind将socket和网络地址和端口绑定起来

    int bind(
      SOCKET                s,
      const struct sockaddr *name,
      int                   namelen
    );

    s:一个未绑定的socket。

    name:指向一个sockaddr对象,用于指定绑定的ip和端口信息。

    namelen:sockaddr的长度,为什么这里还需要指定长度呢,因为name是根据socket的类型来指定不同的结构体的,可能是sockaddr_in(IPv4)或者sockaddr_in6(IPv6)。

    成功返回0

    4. listen将SOCKET设为监听状态,可以被客户端连接

    int listen(
      SOCKET s,
      int    backlog
    );

    s:一个未被连接的socket

    backlog:可以连接的客户端的最大数目,如果指定为SOMAXCONN,则设置为最大的连接数量。

    成功返回0

    5. send通过指定socket发送数据

    int send(
      SOCKET s,
      const char   *buf,
      int    len,
      int    flags
    );

    s:一个已经连接的socket。

    buf:待发送数据

    len:待发送数据的长度

    flags:发送的一个标志设定,一般设为0

    成功返回已发送的字节数目。这个数目可能小于len的。失败返回SOCKET_ERROR。

    6. recv通过指定的socket接受数据

    int recv(
      SOCKET s,
      char   *buf,
      int    len,
      int    flags
    );

    s:一个已经连接的socket

    buf:接收数据的缓存区

    len:缓存区长度

    flags:接受数据的一个标志,一般设为0。

    成功返回已接受数据的长度,失败返回SOCKET_ERROR,如果已经断开连接,返回0

    7. shutdown关闭一个SOCKET的send或者recv功能

    int shutdown(
      SOCKET s,
      int    how
    );

    s:socket

    how:指定该socket的某个功能不需要再使用,可以取值SD_RECEIVE(接收功能),SD_SEND(发送功能),SD_BOTH(发送和接收功能)。

    成功返回0,失败返回SOCKET_ERROR

    8. connect连接到服务端,服务端开启listen后,客户端就可以使用connect进行连接

    int connect(
      SOCKET                s,
      const struct sockaddr *name,
      int                   namelen
    );

    s:一个未连接的socket

    name,namelen:和bind中name,namelen参数一样

    成功返回0,失败返回SOCKET_ERROR

    9. closesocket关闭一个已经存在的socket

    int closesocket(
      SOCKET s
    );

    s:一个待关闭的socket。

    成功返回0,失败返回SOCKET_ERROR

    10. accept接收一个来自客户端的连接

    SOCKET accept(
      SOCKET          s,
      struct sockaddr *addr,
      int             *addrlen
    );

    s:一个已经listen的socket

    addr:用于储存接收到的客户端的sockaddr信息

    addrlen:连接的客户端的sockaddr长度。

    socket通信示例

    服务端和客户端测试代码

    // NetWork1.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <stdio.h>
    #include <tchar.h>
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #pragma comment(lib, "ws2_32.lib")
    #define DEFAULT_BUFLEN 512
    #define DEFAULT_PORT 12345
    
    //启动客户端
    int startClient()
    {
        SOCKET ConnectSocket = INVALID_SOCKET;
        struct sockaddr_in clientService; 
        char *sendbuf = "[Client]:客户端测试文本";
        char recvbuf[DEFAULT_BUFLEN];
        int iResult;
        int recvbuflen = DEFAULT_BUFLEN;
    
    
    
        // 创建一个TCP套接字
        ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket创建失败: %ld
    ", WSAGetLastError());
            WSACleanup();
            return 1;
        }
    
        // 指定连接端口和ip信息
        clientService.sin_family = AF_INET;
        clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
        clientService.sin_port = htons(DEFAULT_PORT);
    
        // 连接服务端
        iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
        if (iResult == SOCKET_ERROR) {
            printf("连接失败: %d
    ", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }
    
        if (ConnectSocket == INVALID_SOCKET) {
            printf("无法连接到指定服务端!
    ");
            WSACleanup();
            return 1;
        }
    
        // 发送一段数据
        iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
        if (iResult == SOCKET_ERROR) {
            printf("发送数据失败: %d
    ", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }
    
        printf("已发送数据大小: %ld
    ", iResult);
    
        // 关闭发送功能,但是仍然可以接收
        iResult = shutdown(ConnectSocket, SD_SEND);
        if (iResult == SOCKET_ERROR) {
            printf("关闭发送功能失败: %d
    ", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }
    
        // 接收数据
        do {
    
            iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
            if (iResult > 0)
                printf("已接收: %d
    ", iResult);
            else if (iResult == 0)
                printf("连接关闭
    ");
            else
                printf("接收数据失败: %d
    ", WSAGetLastError());
    
        } while (iResult > 0);
    
        //关闭套接字
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }
    
    int startServer()
    {
        int iResult;
        SOCKET ListenSocket = INVALID_SOCKET;
        SOCKET ClientSocket = INVALID_SOCKET;
        sockaddr_in service;
        int iSendResult;
        char recvbuf[DEFAULT_BUFLEN];
        int recvbuflen = DEFAULT_BUFLEN;
        char *sendbuf = "[Server]:服务端测试文本";
    
        // 创建TCP套接字
        ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (ListenSocket == INVALID_SOCKET) {
            printf("socket创建失败: %ld
    ", WSAGetLastError());
            WSACleanup();
            return 1;
        }
        // 设定绑定的ip和端口信息
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr("127.0.0.1");
        service.sin_port = htons(DEFAULT_PORT);
    
        // 绑定套接字
        iResult = bind(ListenSocket, (SOCKADDR *)&service, sizeof(service));
        if (iResult == SOCKET_ERROR) {
            printf("套接字绑定失败:%d
    ", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
    
        iResult = listen(ListenSocket, SOMAXCONN);
        if (iResult == SOCKET_ERROR) {
            printf("套接字监听失败: %d
    ", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
    
        // 接受来自客户端的连接
        ClientSocket = accept(ListenSocket, NULL, NULL);
        if (ClientSocket == INVALID_SOCKET) {
            printf("accept failed with error: %d
    ", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
    
        // 关闭服务端套接字,表示不需要再接收新的客户端连接了,但是已经连接的套接字还是能通信
        closesocket(ListenSocket);
    
        do {
            //接收来自客户端的消息
            iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
            if (iResult > 0) {
                printf("已接收数据大小: %d
    ", iResult);
    
                // 发送数据到客户端,这里就是将数据
                iSendResult = send(ClientSocket, sendbuf, (int)strlen(sendbuf), 0);
                if (iSendResult == SOCKET_ERROR) {
                    printf("发送失败: %d
    ", WSAGetLastError());
                    closesocket(ClientSocket);
                    WSACleanup();
                    return 1;
                }
                printf("发送字节大小: %d
    ", iSendResult);
            }
            else if (iResult == 0)
                printf("连接已关闭
    ");
            else {
                printf("接收数据失败: %d
    ", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
        } while (iResult > 0);
    
        //关闭套接字以及清理套接字环境
        closesocket(ClientSocket);
        WSACleanup();
    
        return 0;
    }
    int main(int argc, char *argv[])
    {
        int iResult;
        WSADATA wsaData;
        //初始化套接字环境
        iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != 0) {
            printf("初始化socket环境失败: %d
    ", iResult);
            return 1;
        }
        if (argc == 2 && strcmp(argv[1], "c") == 0)
        {
            //客户端
            return startClient();
        }
        else if (argc == 2 && strcmp(argv[1], "s") == 0)
        {
            //服务端
            return startServer();
        }
        return 1;
    }

    运行结果

    image

    后记

    以上只是一个简单的socket通信示例,所有api调用都是阻塞的,非阻塞调用将在下文写出。

    代码下载

  • 相关阅读:
    反爬虫破解系列-汽车之家利用css样式替换文字破解方法
    保留注释换行的python模块configparser
    python logging 实现的进程安全的文件回滚日志类
    我要自学网视频免登陆观看破解技巧
    修改mac host文件绑定域名
    Position属性四个值:static、fixed、absolute和relative的区别和用法
    vue项目通过webpack打包生成的dist文件放到express环境里运行(vue+webpack+express)
    npm ERR! mathine_call@1.0.0 dev: `webpack-dev-server --inline --progress --config build/webpack.dev.conf.js` npm ERR! Exit status 1
    Do not use built-in or reserved HTML elements as component id: header
    vue 项目 使用sass
  • 原文地址:https://www.cnblogs.com/Reyzal/p/6742553.html
Copyright © 2020-2023  润新知