计算机网络 select模型

相较于同步非阻塞多线程模型  select模型会更节省时间

同步非阻塞多线程模型中 每一个服务者(waiter)套接字都需要区内核区对应一个客人(client)

当客户端发送请求时

每次都需要将服务者链表的所有成员遍历  来找到与之对应的服务者  这样会很费时间  

所以我们定义了一个工具  m_fd  他一个人就可以将你想找的套接字的集合在内核区找  来判断当前哪几个服务者应该响应 

原理大概如下

我们把想查找的 套接字放入集合中   

再将集合交给select管理

select去内核区找相应的套接字   没找到的就从临时集合中删除  找到的保留 

校验 将select处理完的临时集合传回来  判断哪个套接字需要相应

完整代码如下

类声明

#ifndef CTCPMAP_H
#define CTCPMAP_H

#include<list>
#include<winsock2.h>
#include<windows.h>
#include<iostream>
#include<map>
using namespace std;
struct DataInfo
{
    char* pszbuf;
    int  nPacksize;
    int  noffest;
};

class CTCPmap
{
public:
    //构造
    CTCPmap();
    //析构
    ~CTCPmap();
    //初始化网络
    bool InitnewWork();
    //销毁网络
    void DeleteWork();
    //线程函数
    static DWORD WINAPI ThreadSelect(LPVOID lpvoid);
    static DWORD WINAPI ThreadAccept(LPVOID lpvoid);
    static DWORD WINAPI ThreadRecv(LPVOID lpvoid);
    //发送数据
    bool sendDate(SOCKET sockWaiter,char*szbuf,int nLen);
    //接收数据
    void recvDate();

private:
    //监听者句柄
    SOCKET m_soclisten;
    //总线程池
    std::list<HANDLE> my_Threadpool;
    //服务者线程池
    std::list<SOCKET> my_ThreadWaiter;
    //线程结束标识
    bool my_FlagQuit;
    //传服务者sockWaiter用到的容器
    std::map<DWORD,SOCKET> my_map;
    //结构体与套接字的映射
    std::map<SOCKET,DataInfo*> m_sdmap;
    fd_set m_fd;


};

#endif // CTCPMAP_H

类定义

#include "ctcpmap.h"

CTCPmap::CTCPmap()
{

    m_soclisten=0;
    my_FlagQuit=true;
    FD_ZERO(&m_fd);
}
CTCPmap::~CTCPmap()
{

}

bool CTCPmap::InitnewWork()
{

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;


    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {


        DeleteWork();
        return false;
    }



    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {

        WSACleanup();
        return false;
    }




//创建套接字
    m_soclisten=socket(AF_INET,SOCK_STREAM,0);
    if(m_soclisten==INVALID_SOCKET)
    {
       DeleteWork();
        return false;
    }
//绑定
    sockaddr_in addrserver;
    //绑定IP
    addrserver.sin_addr.S_un.S_addr=0;
    addrserver.sin_port=htons(8899);
    addrserver.sin_family=AF_INET;
    if(SOCKET_ERROR==bind(m_soclisten,(sockaddr*)&addrserver,sizeof(addrserver)))
    {
          DeleteWork();
            return false;

    }



//监听

    if(SOCKET_ERROR==listen(m_soclisten,10))

    {
        DeleteWork();
        return false;

    }

    //将套接字加入到集合
    FD_SET(m_soclisten,&m_fd);
    //创建线程
    HANDLE hand=CreateThread(0,0,&ThreadSelect,this,0,0);
    if(hand)
       my_Threadpool.push_back(hand);
//    HANDLE hand=CreateThread(0,0,&ThreadAccept,this,0,0);
//    if(hand)
//        my_Threadpool.push_back(hand);
//    hand=CreateThread(0,0,&ThreadRecv,this,0,0);
//    //创建服务者线程 加入到线程池
//    if(hand)
//        my_Threadpool.push_back(hand);

    return  true;


}

void CTCPmap::DeleteWork()
{
    //线程销毁
    my_FlagQuit=false;
    auto ite=my_Threadpool.begin();
    while(ite!=my_Threadpool.end())
    {
        if(WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
            TerminateThread(*ite,-1);
        CloseHandle(*ite);
        *ite=NULL;
        ite++;
    }

    my_Threadpool.clear();
     //关闭句柄
    if(m_soclisten)
        {
        closesocket(m_soclisten);
        m_soclisten=0;
    }

    //卸载服务器
    WSACleanup();
}
//将集合交给select线程函数
DWORD WINAPI CTCPmap::ThreadSelect(LPVOID lpvoid)
{
    CTCPmap *pthis=(CTCPmap*)lpvoid;
    fd_set fdtemp;
    TIMEVAL tv;
    tv.tv_sec=0;
    tv.tv_usec=100;
    sockaddr_in addrclient;

    int  nRecvNum;
    int  nsize=sizeof(addrclient);
    while(pthis->my_FlagQuit)
        {

        fdtemp =pthis->m_fd;
        //将套接字交给集合
         select(0,&fdtemp,0,0,&tv);
        if(FD_ISSET(pthis->m_soclisten,&fdtemp))
        {
            //代表有人连接我了
            SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);

            cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;

            //对应失败  跳出 继续循环
            if(sockWaiter==INVALID_SOCKET)continue;
            //将waiter加入到集合内
            FD_SET(sockWaiter,&pthis->m_fd);
        }

        //校验
        for(unsigned int i=1;i<pthis->m_fd.fd_count;i++)
        {


            if(FD_ISSET(pthis->m_fd.fd_array[i],&fdtemp))
            {
                DataInfo *pData=pthis->m_sdmap[pthis->m_fd.fd_array[i]];
                //代表waiter 有数据来了
                if(!pData)
                {
                    pData=new DataInfo;
                    pData->noffest=0;
                    pData->nPacksize=0;
                    pthis->m_sdmap[pthis->m_fd.fd_array[i]]=pData;
                    //waiter come
                    nRecvNum=recv(pthis->m_fd.fd_array[i],(char*)&pData->nPacksize,sizeof(int),0);
                    if(pData->nPacksize>0)
                    {
                        pData->pszbuf=new char[pData->nPacksize];
                    }
                }
                else
                {
                    nRecvNum=recv(pthis->m_fd.fd_array[i],pData->pszbuf+pData->noffest,pData->nPacksize,0);
                    if(nRecvNum>0)

                    {
                        pData->noffest+=nRecvNum;
                        pData->nPacksize-=nRecvNum;
                        if(pData->nPacksize==0)
                        {
                            //数据包接受完整
                            cout<<pData->pszbuf<<endl;
                            delete []pData->pszbuf;
                            pData->pszbuf=NULL;

                            delete [] pData;
                            pData=NULL;
                            pthis->m_sdmap[pthis->m_fd.fd_array[i]]=NULL;

                        }
                    }
                }


            }

        }


    }
     return  0;

}

//接收客户端的线程函数
 DWORD WINAPI CTCPmap::ThreadAccept(LPVOID lpvoid)
 {
     CTCPmap *pthis=(CTCPmap*)lpvoid;

     sockaddr_in addrclient;
     int  nsize=sizeof(addrclient);

     u_long iMode=1;
     while(pthis->my_FlagQuit)
     {
         //店长接受客人 分配给服务员  --接受链接--accept();

         //服务者与他的客人对应起来
             SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);

             cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;

             //对应失败  跳出 继续循环
             if(sockWaiter==INVALID_SOCKET)continue;
             //对应成功 线程池添加了一个服务者线程

             //将Waiter 介入到链表 将waiter设置为非阻塞

             ioctlsocket(sockWaiter,FIONBIO,&iMode);

             //将服务者套接字加入到集合
             //FD_SET(sockWaiter,&(pthis->m_fd));

             pthis->my_ThreadWaiter.push_back(sockWaiter);






     }
     return  0;


 }

DWORD WINAPI CTCPmap::ThreadRecv(LPVOID lpvoid)
{
    CTCPmap *pthis=(CTCPmap*)lpvoid;

    while(pthis->my_FlagQuit)
    {
        // 接受
        pthis->recvDate();
        Sleep(10);

    }
    return  0;

}

void CTCPmap::recvDate()//接收函数
{

            // 服务员等客人说话 --recv();
                char *pszbuf=NULL;
                //当前位置标识
                int offset=0;
                //包长度
                int  nPackSize;

                int  nRecvNum;
                SOCKET sockWaiter;



                auto ite=my_ThreadWaiter.begin();
                while(ite!=my_ThreadWaiter.end())
                {
                    sockWaiter=*ite;

                        //jieshoubaodaxiao
                        nRecvNum=recv(sockWaiter,(char*)&nPackSize,sizeof(int ),0);
                        if(nRecvNum<=0)
                        {
                            //判断服务器是否下线
                            if(WSAGetLastError()==10054)
                            {
                                //删除映射
                                auto ite=my_map.begin();
                                while(ite!=my_map.end())
                                {
                                    if(ite->second==sockWaiter)
                                    {
                                        my_map.erase(ite);
                                        closesocket(sockWaiter);
                                        break;
                                    }
                                    //ite++;
                                }

                                break;
                            }
                            continue;
                            ite++;
                        }
                        //获得接受内容
                        pszbuf=new char[nPackSize];
                        while(nPackSize)//看当前剩余的还有多少没接受完
                        {
                            nRecvNum=recv(sockWaiter,pszbuf+offset,nPackSize,0);
                            if(nRecvNum>0)
                            {
                                nPackSize-=nRecvNum;
                                offset+=nRecvNum;
                            }
                        }
                        cout<<"client say:"<<pszbuf<<endl;
                        sendDate(sockWaiter,pszbuf,  nRecvNum);
                        delete []pszbuf;
                        pszbuf=NULL;





                     ite++;
                }




}


bool CTCPmap:: sendDate(SOCKET sockWaiter,char*szbuf,int  nLen)
{


    if(sockWaiter==INVALID_SOCKET||!szbuf||nLen<=0)
        return  false;
    if(send(sockWaiter,(char*)&nLen,sizeof(int ),0)<=0)
     return  false;
    if(send(sockWaiter,szbuf,nLen,0)<=0)
     return  false;

    int  nRecvNum;
    int nRecvNuumSIze=sizeof(szbuf);

    while(my_FlagQuit)
    {
        cin>>szbuf;

        send(sockWaiter,(char*)&nRecvNuumSIze,sizeof(int),0);
        send(sockWaiter,szbuf,sizeof(szbuf),0);

        //recv()
        nRecvNum= recv(sockWaiter,szbuf,sizeof(szbuf),0);
        if(sockWaiter>0)
        {
            cout<<"fuwuqi say:"<<szbuf<<endl;
        }



    }

   return true;

}

主函数

#include <QCoreApplication>
#include<winsock2.h>
#include<iostream>
#include"ctcpmap.h"
using namespace std;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    /*
     *1.选择种类  --WSAStartup();
     *2.雇店长  --创建套接字 socket();
     *3.找地 --绑定 --bind();
     *4.店长宣传 --监听 --listen();
     *5.店长接受客人 分配给服务员  --接受链接--accept();
     *6.客人与服务员 服务员等客人说话 --recv();
     *7.回复 --send();
     *8.下班--closesocket();
     *9.关门--WASCleanup();
     *
     */

    CTCPmap ctcp;
    if(ctcp.InitnewWork())
        cout<<"initnework sucess"<<endl;







    return a.exec();
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值