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

相关文章

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

如何设置vim永久显示行号

《如何设置vim永久显示行号》在Linux环境下,vim默认不显示行号,这在程序编译出错时定位错误语句非常不便,通过修改vim配置文件vimrc,可以在每次打开vim时永久显示行号... 目录设置vim永久显示行号1.临时显示行号2.永www.chinasem.cn久显示行号总结设置vim永久显示行号在li

Linux alias的三种使用场景方式

《Linuxalias的三种使用场景方式》文章介绍了Linux中`alias`命令的三种使用场景:临时别名、用户级别别名和系统级别别名,临时别名仅在当前终端有效,用户级别别名在当前用户下所有终端有效... 目录linux alias三种使用场景一次性适用于当前用户全局生效,所有用户都可调用删除总结Linux

Linux:alias如何设置永久生效

《Linux:alias如何设置永久生效》在Linux中设置别名永久生效的步骤包括:在/root/.bashrc文件中配置别名,保存并退出,然后使用source命令(或点命令)使配置立即生效,这样,别... 目录linux:alias设置永久生效步骤保存退出后功能总结Linux:alias设置永久生效步骤

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Mysql虚拟列的使用场景

《Mysql虚拟列的使用场景》MySQL虚拟列是一种在查询时动态生成的特殊列,它不占用存储空间,可以提高查询效率和数据处理便利性,本文给大家介绍Mysql虚拟列的相关知识,感兴趣的朋友一起看看吧... 目录1. 介绍mysql虚拟列1.1 定义和作用1.2 虚拟列与普通列的区别2. MySQL虚拟列的类型2

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

关于@MapperScan和@ComponentScan的使用问题

《关于@MapperScan和@ComponentScan的使用问题》文章介绍了在使用`@MapperScan`和`@ComponentScan`时可能会遇到的包扫描冲突问题,并提供了解决方法,同时,... 目录@MapperScan和@ComponentScan的使用问题报错如下原因解决办法课外拓展总结@

mysql数据库分区的使用

《mysql数据库分区的使用》MySQL分区技术通过将大表分割成多个较小片段,提高查询性能、管理效率和数据存储效率,本文就来介绍一下mysql数据库分区的使用,感兴趣的可以了解一下... 目录【一】分区的基本概念【1】物理存储与逻辑分割【2】查询性能提升【3】数据管理与维护【4】扩展性与并行处理【二】分区的