单线程select模型

 

阻塞
    阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。
    函数只有在得到结果之后才会返回。
非阻塞
    非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

 

/**
参 数 :int nfds -- maxfd是要监视的最大的文件描述符值+1;
参 数 : fd_set *readfds -- 读文件描述符的集合
参 数 : fd_set *writefds -- 可写文件描述符集合
参 数 : fd_set *exceptfds -- 异常文件描述符集合
参 数 : struct timeval *timeout --超时描述,在一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
返回值 :
说 明 :select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型。
*/

int select(_In_ int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval * timeout);


FD_ZERO(fd_set *fdset);         --将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset);          --用于在文件描述符集合中增加一个新的文件描述符。 
FD_CLR(fd_set *fdset);          --用于在文件描述符集合中删除一个文件描述符。 
FD_ISSET(int fd,fd_set *fdset); --用于测试指定的文件描述符是否在该集合中。

 

服务端
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include<windows.h> #include<WinSock2.h> #pragma comment(lib,"ws2_32.lib") #else #include<unistd.h> //uni std #include<arpa/inet.h> #include<string.h> #define SOCKET int #define INVALID_SOCKET (SOCKET)(~0) #define SOCKET_ERROR (-1) #endif #include<stdio.h> #include<thread> #include<vector>
std::vector<SOCKET> g_clients; int processor(SOCKET _cSock) {
  // 处理业务
return 0; } int main() { #ifdef _WIN32 //启动Windows socket 2.x环境 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); //------------ #endif //-- 用Socket API建立简易TCP服务端 // 1 建立一个socket 套接字 SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 2 bind 绑定用于接受客户端连接的网络端口 sockaddr_in _sin = {}; _sin.sin_family = AF_INET; _sin.sin_port = htons(4567);//host to net unsigned short #ifdef _WIN32 _sin.sin_addr.S_un.S_addr = INADDR_ANY;//inet_addr("127.0.0.1"); #else _sin.sin_addr.s_addr = INADDR_ANY; #endif if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin))) { printf("错误,绑定网络端口失败...\n"); } else { printf("绑定网络端口成功...\n"); } // 3 listen 监听网络端口 if (SOCKET_ERROR == listen(_sock, 5)) { printf("错误,监听网络端口失败...\n"); } else { printf("监听网络端口成功...\n"); } while (true) { //伯克利套接字 BSD socket //描述符(socket) 集合
//fd_set 最大值为默认64, 所以一次最大能处理64个链接 fd_set fdRead; fd_set fdWrite; fd_set fdExp; //清理集合 FD_ZERO(&fdRead); FD_ZERO(&fdWrite); FD_ZERO(&fdExp); //将描述符(socket)加入集合 FD_SET(_sock, &fdRead); FD_SET(_sock, &fdWrite); FD_SET(_sock, &fdExp); SOCKET maxSock = _sock; for (int n = (int)g_clients.size() - 1; n >= 0; n--) { FD_SET(g_clients[n], &fdRead); if (maxSock < g_clients[n]) { maxSock = g_clients[n]; } } ///nfds 是一个整数值 是指fd_set集合中所有描述符(socket)的范围,而不是数量 ///既是所有文件描述符最大值+1 在Windows中这个参数可以写0 //select 函数不设置 timeval 时,select是阻塞的,直到有数据。 //select 函数设置 timeval 时,超时即返回、select是非阻塞的。 timeval t = { 1,0 }; int ret = select(maxSock + 1, &fdRead, &fdWrite, &fdExp, &t); if (ret < 0) { printf("select任务结束。\n"); break; } //判断描述符(socket)是否在集合中 if (FD_ISSET(_sock, &fdRead)) { FD_CLR(_sock, &fdRead); // 4 accept 等待接受客户端连接 sockaddr_in clientAddr = {}; int nAddrLen = sizeof(sockaddr_in); SOCKET _cSock = INVALID_SOCKET; #ifdef _WIN32 _cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen); #else _cSock = accept(_sock, (sockaddr*)&clientAddr, (socklen_t *)&nAddrLen); #endif if (INVALID_SOCKET == _cSock) { printf("错误,接受到无效客户端SOCKET...\n"); } else { g_clients.push_back(_cSock); printf("新客户端加入:socket = %d,IP = %s \n", (int)_cSock, inet_ntoa(clientAddr.sin_addr)); } } for (int n = (int)g_clients.size() - 1; n >= 0; n--) { if (FD_ISSET(g_clients[n], &fdRead)) { if (-1 == processor(g_clients[n])) { auto iter = g_clients.begin() + n; if (iter != g_clients.end()) { g_clients.erase(iter); } } } } //printf("空闲时间处理其它业务..\n"); } #ifdef _WIN32 for (int n = (int)g_clients.size() - 1; n >= 0; n--) { closesocket(g_clients[n]); } // 8 关闭套节字closesocket closesocket(_sock); //------------ //清除Windows socket环境 WSACleanup(); #else for (int n = (int)g_clients.size() - 1; n >= 0; n--) { close(g_clients[n]); } // 8 关闭套节字closesocket close(_sock); #endif printf("已退出。\n"); getchar(); return 0; }

 

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<windows.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include<unistd.h> //uni std
#include<arpa/inet.h>
#include<string.h>

#define SOCKET int
#define INVALID_SOCKET  (SOCKET)(~0)
#define SOCKET_ERROR            (-1)
#endif

#include<stdio.h>
#include<thread>

bool g_bRun = true;

// 主线程中处理数据
int processor(SOCKET _cSock)
{
    //缓冲区
    char szRecv[4096] = {};
    // 接收数据
    // recv to do
    return 0;
}

//线程中发送数据
void sendThread(SOCKET sock)
{
    while (true)
    {
        char cmdBuf[256] = {};
        // 发送数据
        // send to do 
    }
}

int main()
{
#ifdef _WIN32
    //启动Windows socket 2.x环境
    WORD ver = MAKEWORD(2, 2);
    WSADATA dat;
    WSAStartup(ver, &dat);
#endif
    //------------
    //-- 用Socket API建立简易TCP客户端
    // 1 建立一个socket
    SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == _sock)
    {
        printf("错误,建立Socket失败...\n");
    }
    else {
        printf("建立Socket成功...\n");
    }
    // 2 连接服务器 connect
    sockaddr_in _sin = {};
    _sin.sin_family = AF_INET;
    _sin.sin_port = htons(4567);
#ifdef _WIN32
    _sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
#else
    _sin.sin_addr.s_addr = inet_addr("192.168.74.1");
#endif
    int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
    if (SOCKET_ERROR == ret)
    {
        printf("错误,连接服务器失败...\n");
    }
    else {
        printf("连接服务器成功...\n");
    }
    //启动线程处理发送业务
    std::thread t1(sendThread, _sock);
    t1.detach();

    while (g_bRun)
    {
        fd_set fdReads;
        FD_ZERO(&fdReads);
        FD_SET(_sock, &fdReads);
        timeval t = { 1,0 };
        int ret = select(_sock + 1, &fdReads, 0, 0, &t);
        if (ret < 0)
        {
            printf("select任务结束1\n");
            break;
        }
        if (FD_ISSET(_sock, &fdReads))
        {
            FD_CLR(_sock, &fdReads);

            if (-1 == processor(_sock))
            {
                printf("select任务结束2\n");
                break;
            }
        }
        //printf("空闲时间处理其它业务..\n");
    }
    // 关闭套节字closesocket
#ifdef _WIN32
    closesocket(_sock);
    //清除Windows socket环境
    WSACleanup();
#else
    close(_sock);
#endif
    printf("已退出。\n");
    getchar();
    return 0;
}

 

posted @ 2018-11-14 23:24  osbreak  阅读(1323)  评论(0编辑  收藏  举报