Windows C++ TCP开发(使用select函数以及设置非阻塞/Reuse属性)

2024-03-14 20:20

本文主要是介绍Windows C++ TCP开发(使用select函数以及设置非阻塞/Reuse属性),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、select官方函数说明:

语法

C++

int WSAAPI select([in]      int           nfds,[in, out] fd_set        *readfds,[in, out] fd_set        *writefds,[in, out] fd_set        *exceptfds,[in]      const timeval *timeout
);

参数

[in] nfds

已忽略。 包含 nfds 参数只是为了与 Berkeley 套接字兼容。

[in, out] readfds

一个可选指针,指向要检查的一组套接字的可读性。

[in, out] writefds

指向要检查可写性的一组套接字的可选指针。

[in, out] exceptfds

指向要检查错误的一组套接字的可选指针。

[in] timeout

选择等待的最长时间,以 TIMEVAL 结构的形式提供。 将阻止操作的 超时 参数设置为 null 。

返回值

select 函数返回fd_set结构中已就绪并包含的套接字句柄总数;如果时间限制过期,则返回 0;如果发生错误,则返回SOCKET_ERROR。 如果返回值SOCKET_ERROR,则 WSAGetLastError 可用于检索特定的错误代码。

 2、阻塞设置

    unsigned   long cmd = 1;
    nRet = ioctlsocket(sclient, FIONBIO, &cmd);
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to set FIONBIOsocket!\n");
        WSACleanup();
        return 0;
    }

3、设置端口 Reuse属性

    bool bReuseAddr = true;
    //设置端口SO_REUSEADDR属性,端口释放后可以立即被使用。
    nRet = setsockopt(sclient, SOL_SOCKET, SO_REUSEADDR, (char *)&bReuseAddr, sizeof(bReuseAddr));
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to set resueaddr socket!\n");
        WSACleanup();
        return 0;
    }

 什么是resuse属性:端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中,必须等超时时间大概是2分钟。

4、TCP代码+select
4.1 TCP 服务端代码

#include <iostream>
#include <stdio.h>  
#include <string.h>  
#include <WINSOCK2.H>  

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

#define INT_SERVER_PORT 8888  
#define STR_SERVER_IP "127.0.0.1"  
#define INT_DATABUFFER_SIZE 100  

void main(void)
{
    //与WSACleanup函数配对使用
    WORD dwVersion = MAKEWORD(2, 2);
    WSAData wsaData;
    WSAStartup(WINSOCK_VERSION, &wsaData);

    //创建套接字
    SOCKET sockServer = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == sockServer)
    {
        printf("Failed to create socket!\n");
        WSACleanup();
        return;
    }

    //本地地址初始化
    sockaddr_in addrServer;
    memset(&addrServer, 0, sizeof(sockaddr_in));
    addrServer.sin_family = AF_INET;
    addrServer.sin_port = htons(INT_SERVER_PORT);
    addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

    int nRet = 0;
    bool bReuseAddr = true;
    //设置端口SO_REUSEADDR属性,端口释放后可以立即被使用。
    nRet = setsockopt(sockServer, SOL_SOCKET, SO_REUSEADDR, (char *)&bReuseAddr, sizeof(bReuseAddr));
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to set resueaddr socket!\n");
        WSACleanup();
        return;
    }

    //设置端口非阻塞
    unsigned   long cmd = 1;
    nRet = ioctlsocket(sockServer,FIONBIO,&cmd);
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to set resueaddr socket!\n");
        WSACleanup();
        return;
    }

    nRet = bind(sockServer, (sockaddr *)&addrServer, sizeof(addrServer));
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to bind address!\n");
        WSACleanup();
        return;
    }

    if (0 != listen(sockServer, 5))
    {
        printf("Failed to listen client!\n");
        WSACleanup();
        return;
    }

    UINT i = 0;
    fd_set fd;
    FD_ZERO(&fd);
    FD_SET(sockServer, &fd);

    /*
    timeval tm;
    tm.tv_sec = 0;
    tm.tv_usec = 1000;
    */
    while (1)
    {
        fd_set fdOld = fd;
        nRet = select(0, &fdOld, NULL, NULL,/*&tm*/NULL);
        if (0 <= nRet)
        {
            for (i = 0; i < fd.fd_count; i++)
            {
                if (FD_ISSET(fd.fd_array[i], &fdOld))
                {
                    if (fd.fd_array[i] == sockServer)//接收到新的连接
                    {
                        SOCKET sockAccept;
                        sockaddr_in addrAccept;
                        int iAcceptLen = sizeof(addrAccept);
                        memset(&addrAccept, 0, sizeof(addrAccept));
                        sockAccept = accept(sockServer, (sockaddr *)&addrAccept, &iAcceptLen);
                        if (INVALID_SOCKET != sockAccept)
                        {
                            send(sockAccept, "Hello Client", strlen("Hello Client"), 0);
                            FD_SET(sockAccept, &fd);
                            printf("%s:%d has connected server!\n", inet_ntoa(addrAccept.sin_addr),
                                ntohs(addrAccept.sin_port));
                        }
                    }
                    else //非服务器,接收数据(因为fd是读数据集)  
                    {
                        char szDataBuff[INT_DATABUFFER_SIZE];
                        int iRecvSize;
                        memset(szDataBuff, 0, INT_DATABUFFER_SIZE);
                        iRecvSize = recv(fd.fd_array[i], szDataBuff, INT_DATABUFFER_SIZE, 0);
                        if (SOCKET_ERROR == iRecvSize)
                        {
                            closesocket(fd.fd_array[i]);
                            FD_CLR(fd.fd_array[i], &fd);
                            i--;
                            printf("Failed to recv data ,or Client has disconnected, errorcode:%d.\n", WSAGetLastError());
                            continue;
                        }

                        if (0 == iRecvSize)
                        {
                            //客户socket关闭  
                            closesocket(fd.fd_array[i]);
                            FD_CLR(fd.fd_array[i], &fd);
                            i--;
                        }

                        if (0 < iRecvSize)
                        {
                            //打印接收的数据  
                            printf("recv data:%s\n",szDataBuff);
                        }
                    }
                }
            }
        }
        else if (SOCKET_ERROR == nRet)
        {
            printf("Faild to select sockt in server! Error Code[%d]\n", WSAGetLastError());
            Sleep(100);
        }
    }
    WSACleanup();
}
 

4.2 TCP客户端代码 

#include <iostream>
#include <stdio.h>  
#include <string.h>  
#include <WINSOCK2.H>  
using namespace std;
#pragma comment(lib, "ws2_32.lib")

int main()
{
    //与WSACleanup函数配对使用
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA data;
    if (WSAStartup(sockVersion, &data) != 0)
    {
        return 0;
    }

    //创建客户端套接字
    SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sclient == INVALID_SOCKET)
    {
        printf("invalid socket!");
        return 0;
    }

    //初始化SOCKET结构体
    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(8888);
    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
    {  //连接失败 
        printf("connect error !");
        closesocket(sclient);
        return 0;
    }

    int nRet = 0;
    bool bReuseAddr = true;
    //设置端口SO_REUSEADDR属性,端口释放后可以立即被使用。
    nRet = setsockopt(sclient, SOL_SOCKET, SO_REUSEADDR, (char *)&bReuseAddr, sizeof(bReuseAddr));
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to set resueaddr socket!\n");
        WSACleanup();
        return 0;
    }

    //设置端口非阻塞
    unsigned   long cmd = 1;
    nRet = ioctlsocket(sclient, FIONBIO, &cmd);
    if (SOCKET_ERROR == nRet)
    {
        printf("Failed to set resueaddr socket!\n");
        WSACleanup();
        return 0;
    }

    //发送数据
    send(sclient, "Hello Server", strlen("Hello Server"), 0);

    //接收数据
    while (true)
    {
        fd_set fd;
        FD_ZERO(&fd);
        FD_SET(sclient, &fd);
        nRet = select(0, &fd, NULL, NULL,NULL);
        if (0 <= nRet)
        {
            char recData[255];
            int ret = recv(sclient, recData, 255, 0);
            if (ret > 0) {
                recData[ret] = 0x00;
                printf("Recv :%s\n",recData);
            }
            else
            {
                printf("Socket Error or Socket has closed\n");
                break;
            }
        }
        else if (nRet == SOCKET_ERROR)
        {
            printf("Faild to select sockt in server! Error Code[%d]\n", WSAGetLastError());
            break;
        }
    }
    closesocket(sclient);
    WSACleanup();
    return 0;
}

这篇关于Windows C++ TCP开发(使用select函数以及设置非阻塞/Reuse属性)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/809619

相关文章

POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能

《POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能》ApachePOI是一个流行的Java库,用于处理MicrosoftOffice格式文件,提供丰富API来创建、读取和修改O... 目录前言:Apache POIEasyPoiEasyExcel一、EasyExcel1.1、核心特性

Java 如何创建和使用ExecutorService

《Java如何创建和使用ExecutorService》ExecutorService是Java中用来管理和执行多线程任务的一种高级工具,可以有效地管理线程的生命周期和任务的执行过程,特别是在需要处... 目录一、什么是ExecutorService?二、ExecutorService的核心功能三、如何创建

Python+Tkinter实现Windows Hosts文件编辑管理工具

《Python+Tkinter实现WindowsHosts文件编辑管理工具》在日常开发和网络调试或科学上网场景中,Hosts文件修改是每个开发者都绕不开的必修课,本文将完整解析一个基于Python... 目录一、前言:为什么我们需要专业的Hosts管理工具二、工具核心功能全景图2.1 基础功能模块2.2 进

使用FileChannel实现文件的复制和移动方式

《使用FileChannel实现文件的复制和移动方式》:本文主要介绍使用FileChannel实现文件的复制和移动方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录使用 FileChannel 实现文件复制代码解释使用 FileChannel 实现文件移动代码解释

Java中的getBytes()方法使用详解

《Java中的getBytes()方法使用详解》:本文主要介绍Java中getBytes()方法使用的相关资料,getBytes()方法有多个重载形式,可以根据需要指定字符集来进行转换,文中通过代... 目录前言一、常见重载形式二、示例代码三、getBytes(Charset charset)和getByt

Java使用Stream流的Lambda语法进行List转Map的操作方式

《Java使用Stream流的Lambda语法进行List转Map的操作方式》:本文主要介绍Java使用Stream流的Lambda语法进行List转Map的操作方式,具有很好的参考价值,希望对大... 目录背景Stream流的Lambda语法应用实例1、定义要操作的UserDto2、ListChina编程转成M

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

使用Java编写一个字符脱敏工具类

《使用Java编写一个字符脱敏工具类》这篇文章主要为大家详细介绍了如何使用Java编写一个字符脱敏工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、字符脱敏工具类2、测试工具类3、测试结果1、字符脱敏工具类import lombok.extern.slf4j.Slf4j

pandas DataFrame keys的使用小结

《pandasDataFramekeys的使用小结》pandas.DataFrame.keys()方法返回DataFrame的列名,类似于字典的键,本文主要介绍了pandasDataFrameke... 目录Pandas2.2 DataFrameIndexing, iterationpandas.DataF