【网络编程】select模型

简介: 【网络编程】select模型

我的小站——半生瓜のblog


@TOC

select模型

特点

  1. 解决基本c/s模型中,accept,rcev傻等的问题。

    • 傻等阻塞
    • 执行阻塞 send recv accept 在执行的复制粘贴的过程中都是阻塞的。

    (网络模型就是解决阻塞问题的)

  2. 实现多个客户端链接,与多个客户端分别通信。
  3. 用于服务器,因为客户端就一个socket。

服务器端

网络头文件 网络库
打开网络库
校验版本
创建socket
绑定地址与端口
开始监听
 
select    

逻辑

  1. 每个客户端都有socket,服务器也有自己的socket,将所有的socket装进一个数据结构里,即数组。
  2. 通过select函数,遍历1中的socket数组,当某个socket有相应,select就会通过其参数/返回值反馈出来。
  3. 处理。如果见得到的是服务器socket,那就有客户端链接,调用accept。如果检测到客户端socket,那就是客户端请求通信,调用send或者recv。

定义一个装客户端的socket结构体

fd_set

是网络库中定义好的类型。

typedef struct fd_set {
        //几个有效的
        u_int fd_count;               /* how many are SET? */
        //数组
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

默认FD_SERSIZE 是64,重新宏定义要写在网络库前。
尽量不要太大,大用户量应该用更高级的网络模型。
select模型应用就是小用户量访问,几十几百,简单方便。
    
    fd_set socketClient;

四个参数宏

FD_ZERO
    #define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
    将定义好的集合清零
    
    FD_ZERO(&socketClient);
FD_SET 
    #define FD_SET(fd, set) do { \
    u_int __i; \
    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
        if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { \
            break; \
        } \
    } \
    if (__i == ((fd_set FAR *)(set))->fd_count) { \
        if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
            ((fd_set FAR *)(set))->fd_array[__i] = (fd); \
            ((fd_set FAR *)(set))->fd_count++; \
        } \
    } \
} while(0, 0)
   向集合中添加socket  
FD_CLR
   #define FD_CLR(fd, set) do { \
    u_int __i; \
    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \
        if (((fd_set FAR *)(set))->fd_array[__i] == fd) { \
            while (__i < ((fd_set FAR *)(set))->fd_count-1) { \
                ((fd_set FAR *)(set))->fd_array[__i] = \
                    ((fd_set FAR *)(set))->fd_array[__i+1]; \
                __i++; \
            } \
            ((fd_set FAR *)(set))->fd_count--; \
            break; \
        } \
    } \
} while(0, 0)
    从集合中删除某个元素,要手动释放,closesocket(socketServer)
    同链表删除。
FD_ISSET
    #define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set))
    判断集合中是否有某个元素
    有-返回非0
    没有-返回0

select

int WSAAPI select(
    int nfds,
    fd_set *readfds,
    fd_set *writefds,
    fd_set *exceptfds,
);

作用

监视socket集合,如果某个socket发生事件,(链接或者收发数据),通过返回值以及参数告诉我们。

参数1

Ignored忽略,填0,仅为了兼容(向下兼容性)Berkeley sockets。

参数2

检查是否有可读的scoket。(是否有消息recv/accept/)

即客户端发来消息了,该socket就会被设置。

初始化所有的socket,通过select投放给系统,系统将有事件发生的socket再复制回来,调用后,这个参数就只剩下有请求的socket。

返回有响应的socket。用个中间变量接收。

参数3

检查是否有可写的socket。

从头到尾遍历出来。

即,使可以给哪些客户端套接字发消息,即send,只要链接成功建立起来了,该客户端套接字就是可写的。

初始化所有的socket,通过select投放给系统,系统将可以写的socket在复制回来,调用后,这个参数就是装着可以被send数据的客户端socket。

参数4

检查套接字上的异常错误,用法同参数23。将所有的socket投放进去。

得到异常套接字上的具体错误码。

getsockopt(socket,SOL_SOCKET,SO_ERROR,buf,buflen);

参数5

最大等待时间,比如当客户端没有请求时,那么select函数可以等一会儿,一段时间过后,还没有,就继续执行select下面的语句,如果有了,就立刻执行下面的语句。

TIMEVAL
    tv_sec 秒
    tv_usec 微秒
    0 0非阻塞状态,立刻返回
    3 4那就再无客户端相应的情况下等待3秒4微秒
NULL
    select完全阻塞,知道客户端有反应,我才继续

返回值

0 客户端在等待时间内没有反应  处理——continue>0 有客户端请求交流了SOCKET_ERROR 发生了错误        得到错误码WSAGetLaseError()

流程总结

socket集合    socket判断有没有相应的        返回0,没有,继续挑        返回>0,有相应                    可读的accept                          recv                    可写的send                    异常的getsockopt        SOCK_ERROR

select是阻塞的。

不等待——执行阻塞

半等待——执行阻塞+软阻塞

全等待——执行阻塞+硬阻塞 死等

完整代码

(仅熟悉流程)

#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<WinSock2.h> 
#pragma comment(lib,"Ws2_32.lib")



//装所有的socket
fd_set allSocket;

BOOL WINAPI fun(DWORD dwCtrlType)
{
    switch (dwCtrlType)
    {

    case CTRL_CLOSE_EVENT:
        for (u_int i = 0; i < allSocket.fd_count; i++)
        {
            closesocket(allSocket.fd_array[i]);
        }
        WSACleanup();
    }
    return TRUE;
}

int main(void)
{
    //投递一个监视
    //关闭事件
    //控制台点叉退出
    SetConsoleCtrlHandler(fun, TRUE);    


    WORD wdVersion = MAKEWORD(2, 2);
    WSADATA wdSockMsg;
    int nRes = WSAStartup(wdVersion, &wdSockMsg);

    if (nRes != 0)
    {
        printf("网络库打开失败");
        return 0;
    }

    if (HIBYTE(wdSockMsg.wVersion) != 2 || LOBYTE(wdSockMsg.wVersion) != 2)
    {
        printf("版本不对");
        WSACleanup();
        return 0;
    }

    SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socketServer == INVALID_SOCKET)
    {
        printf("创建服务器socket失败");
        int a = WSAGetLastError();
        WSACleanup();
        return 0;
    }
    SOCKADDR_IN si;
    si.sin_family = AF_INET;
    si.sin_port = htons(12345);
    si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(si)))
    {
        printf("绑定错误");
        int a = WSAGetLastError();
        closesocket(socketServer);
        WSACleanup();
        return 0;
    }

    if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
    {
        int a = WSAGetLastError();
        closesocket(socketServer);
        WSACleanup();
        return 0;
    }


    //清零
    FD_ZERO(&allSocket);
    //把服务器装进去
    FD_SET(socketServer, &allSocket);
    
    while (1)
    {
        //可读
        fd_set readSocket = allSocket;
        //可写
        fd_set writeSocket = allSocket;
        FD_CLR(socketServer, &writeSocket);
        fd_set errorSocket = allSocket;



        //时间段
        struct timeval st;
        st.tv_sec = 3;
        st.tv_usec = 0;
        //不用哪个哪个位置就写NULL
        int nRes = select(0, &readSocket, &writeSocket, &errorSocket, &st);
        if (nRes == 0)//没有响应的socket
        {
            continue;
        }
        else if (nRes > 0)
        {

            for (u_int i = 0; i < errorSocket.fd_count; i++)
            {
                char str[100] = { 0 };
                int len = 99;
                if (SOCKET_ERROR == getsockopt(errorSocket.fd_array[i], SOL_SOCKET, SO_ERROR,str,&len))
                {
                    printf("无法得到错误信息\n");
                }
                printf("%s\n", str);
                
            }

            for(u_int i = 0;i<writeSocket.fd_count;i++)
            {
                //printf("服务器%d %d:可写\n", socketServer, writeSocket.fd_array[i]);
                if (SOCKET_ERROR == send(writeSocket.fd_array[i], "ok", 2, 0))
                {
                    //正常 大于0 socket_error 下线0
                    int a = WSAGetLastError();
                }
            }

            //有响应
            //遍历socket
            for (u_int i = 0; i < readSocket.fd_count; i++)
            {
                if (readSocket.fd_array[i] == socketServer)
                {
                    //有链接(响应)-accept
                    SOCKET socketClient = accept(socketServer, NULL, NULL);
                    if (socketClient == SOCKET_ERROR)
                    {
                        //链接出错
                        continue;
                    }
                    FD_SET(socketClient, &allSocket);
                    //SEND
                    send(readSocket.fd_array[i], "服务器链接成功!", sizeof("服务器链接成功"),0);
                }
                else
                {
                    char strBuf[1500] = { 0 };
                    //客户端socket
                    int nRecv = recv(readSocket.fd_array[i], strBuf, 1500, 0);
                    if (nRecv == 0)
                    {
                        //客户端下线了
                        //从集合中去掉        
                        SOCKET socketTemp = readSocket.fd_array[i];
                        FD_CLR(readSocket.fd_array[i],&allSocket);
                        //释放
                        closesocket(socketTemp);

                    }
                    else if(nRecv > 0)
                    {
                        //接收到了消息
                        printf(strBuf);
                    }
                    else
                    {
                        //强制下线10054
                        
                        //出错了SOCK_ERROR
                        int a = WSAGetLastError();
                        switch (a)
                        {
                            case 10054:
                            {
                            SOCKET socketTemp = readSocket.fd_array[i];
                            FD_CLR(readSocket.fd_array[i], &allSocket);
                            closesocket(socketTemp);
                            }
                        }
                        printf("%d", a);
                    }
                }
            }
        }
        else
        {
            //发生错误了
            break;
        }
    }




    //释放socket集合
    for (u_int i = 0; i < allSocket.fd_count; i++)
    {
        closesocket(allSocket.fd_array[i]);
    }
    
    WSACleanup();//正常关闭
    system("pause");
    return 0;
}
相关文章
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于BP神经网络的苦瓜生长含水量预测模型matlab仿真
本项目展示了基于BP神经网络的苦瓜生长含水量预测模型,通过温度(T)、风速(v)、模型厚度(h)等输入特征,预测苦瓜的含水量。采用Matlab2022a开发,核心代码附带中文注释及操作视频。模型利用BP神经网络的非线性映射能力,对试验数据进行训练,实现对未知样本含水量变化规律的预测,为干燥过程的理论研究提供支持。
|
11天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
36 2
|
12天前
|
运维 网络协议 算法
7 层 OSI 参考模型:详解网络通信的层次结构
7 层 OSI 参考模型:详解网络通信的层次结构
33 1
|
1月前
|
网络协议 前端开发 Java
网络协议与IO模型
网络协议与IO模型
网络协议与IO模型
|
1月前
|
机器学习/深度学习 网络架构 计算机视觉
目标检测笔记(一):不同模型的网络架构介绍和代码
这篇文章介绍了ShuffleNetV2网络架构及其代码实现,包括模型结构、代码细节和不同版本的模型。ShuffleNetV2是一个高效的卷积神经网络,适用于深度学习中的目标检测任务。
75 1
目标检测笔记(一):不同模型的网络架构介绍和代码
|
23天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
1月前
|
安全 NoSQL Java
一文搞懂网络通信的基石✅IO模型与零拷贝
【10月更文挑战第1天】本文深入探讨了网络通信中的IO模型及其优化方法——零拷贝技术。首先介绍了IO模型的概念及五种常见类型:同步阻塞、同步非阻塞、多路复用、信号驱动和异步IO模型。文章详细分析了每种模型的特点和适用场景,特别是多路复用和异步IO在高并发场景中的优势。接着介绍了零拷贝技术,通过DMA直接进行数据传输,避免了多次CPU拷贝,进一步提升了效率。最后总结了各种模型的优缺点,并提供了相关的代码示例和资源链接。
一文搞懂网络通信的基石✅IO模型与零拷贝
|
28天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
72 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
1月前
|
开发者
什么是面向网络的IO模型?
【10月更文挑战第6天】什么是面向网络的IO模型?
22 3
|
1月前
|
数据挖掘 开发者
网络IO模型
【10月更文挑战第6天】网络IO模型
42 3
下一篇
无影云桌面