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

相关文章

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同