zoukankan      html  css  js  c++  java
  • I/O模型 之Select 模型; Select管理TCP server

    一:Tcp Server

    1: 当我们的Server接入一个客户端进来以后,就要管理好负责从客户端来接收数据,但是服务器要对多个客户端从不能一直等待这一个客户端有数据进来;

    2: 我们的server还要读写数据,总不能一直等着有新的用户进来;

    3: 需要一种方式来管理, select模型来管理我们的Tcp Server;

    1> 创建一个句柄集合,这个集合里面有要管理的所有的socket(句柄);

    2> 使用select等待这些句柄上面;

    3> 监听socket有数据进来了,表示有新的连接;

    4> 已经建立好连接的socket,有数据进来了,表示服务器收到了客户端的数据;

    5> 如果客户端关闭了socket或断网了select也能监测得到;

    二:Select

    1: 创建一个监听socket:

    int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (s == INVALID_SOCKET) { return -1; }

    struct sockaddr_in sockaddr;

    sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    sockaddr.sin_family = AF_INET;

    sockaddr.sin_port = htons(port);

    int ret = bind(s, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));

    ret = listen(s, 1);

    2: 配置句柄管理集合:

    fd_set socket_set;

    FD_ZERO(&socket_set);

    FD_SET(s, &socket_set);

    FD_CLR(sock, socket_set);

    FD_ISSET(s, &socket_set)

    int ret = select(0, &socket_set, NULL, NULL, NULL);

    三:TCP server 代码:

    // Server.cpp : 定义控制台应用程序的入口点。
    //

    #include "stdafx.h"
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #ifdef WIN32
    #include <WinSock2.h>
    #include <Windows.h>
    #pragma comment (lib,"ws2_32.lib")
    #endif
    #define MAX_BUF_LEN 4096
    static int client_fd[4096];
    static int socket_count;
    static unsigned char recv_buf[MAX_BUF_LEN];
    int main(int argc, char** argv)
    {
    int ret;
    // 配置windows
    #ifdef WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2,2);
    ret = WSAStartup(wVersionRequested,&wsaData);
    if (ret != 0)
    {
    system("pause");
    return -1;
    }
    #endif
    // step1 创建一个监听socket
    int s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // TCP
    if (s == INVALID_SOCKET)
    {
    goto failed;
    }

    // ip 地址+端口
    struct sockaddr_in sockaddr;
    sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(8080); //127.0.0.1:6080 端口
    ret = bind(s, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));
    if (ret != 0)
    goto failed;

    // 开始监听
    ret = listen(s,1);
    fd_set set; // 定义一个句柄集合
    while (1)
    {
    // 清空一下句柄语句
    FD_ZERO(&set);
    FD_SET(s,&set);// 将我们监听的句柄加入到等待的集合上

    // 其他客户端进入来的socket加入到我们的句柄集合
    for (int j = 0; j < socket_count;j++)
    {
    FD_SET(client_fd[j],&set);
    }
    ret = select(0,&set,NULL,NULL,NULL);
    if (ret < 0)
    {
    printf("select error ");
    }
    else if (ret == 0)
    {
    printf("select timeout ");
    continue;
    }
    // 判断是哪个句柄发生了事件
    if (FD_ISSET(s, &set)) // 发送过来的是连接请求
    {
    // 等待客户端接入进来
    struct sockaddr_in c_address; //客户端IP地址
    int address_len = sizeof(c_address);
    // cs 服务端为客户端创建的配对的socket;
    // c_address 客户端的IP地址和端口
    printf("waitting... ");
    int cs = accept(s, (struct sockaddr*) &c_address, &address_len);
    printf("new client %s :%d ", inet_ntoa(c_address.sin_addr));
    client_fd[socket_count] = cs;
    socket_count++;
    continue;
    }

    for (int j = 0; j < socket_count; j++)
    {
    if (FD_ISSET(client_fd[j], &set)&& client_fd[j] != INVALID_SOCKET)
    {
    int len = recv(client_fd[j], (char*)recv_buf, MAX_BUF_LEN, 0);
    if (len <= 0)
    {
    closesocket(client_fd[j]);
    client_fd[j] = INVALID_SOCKET;
    }
    else
    {
    recv_buf[len] = 0;
    printf("recv:%s ", recv_buf);
    send(client_fd[j], (char*)recv_buf,len,0);
    }
    }
    }

    }
    failed:
    if (s != INVALID_SOCKET)
    {
    closesocket(s);
    }

    #ifdef WIN32
    WSACleanup();
    #endif
    return 0;
    }

    TCP Client 代码:

    #include "stdafx.h"
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #ifdef WIN32
    #include <WinSock2.h>
    #include <Windows.h>
    #pragma comment (lib,"WSOCK32.LIB")
    #endif


    int main()
    {
    int ret;
    // 配置windows
    #ifdef WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    ret = WSAStartup(wVersionRequested, &wsaData);
    if (ret != 0)
    {
    system("pause");
    return -1;
    }
    #endif
    // step1 创建一个监听socket
    int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // TCP
    if (s == INVALID_SOCKET)
    {
    goto failed;
    }

    // ip 地址+端口
    struct sockaddr_in sockaddr;
    sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(8080); //127.0.0.1:6080 端口
    // 发送连接请求到服务的的监听socket
    ret = connect(s, ((struct sockaddr*) &sockaddr),sizeof(sockaddr));

    char buf[11];
    memset(buf,0,11);
    send(s,"Hello",5,0);
    recv(s,buf,5,0);
    printf("%s ",buf);
    if (ret != 0)
    goto failed;
    failed:
    if (s != INVALID_SOCKET)
    {
    closesocket(s);
    s = INVALID_SOCKET;
    }

    #ifdef WIN32
    WSACleanup();
    #endif

    return 0;
    }

     四:Select缺点

    1: 性能不好,每次有事件的时候都要遍历所有的句柄,然后查是哪个句柄的事件;

    2: 能够管理的句柄的数目是有限制的, 2048个

    3: socket的读和写仍然是同步的, 我们发送和接受数据的时候会等在网卡上面;

  • 相关阅读:
    使用事件驱动模型实现高效稳定的网络服务器程序
    Muduo 多线程模型:一个 Sudoku 服务器演变
    淘宝李晓拴:淘宝网PHP电子商务应用
    又一随机视频聊天网站内侧了,名字叫QQ偶遇,地址为:http://qq.17ouyu.com/
    又一随机视频聊天网站内侧了,地址为:http://www.17ouyu.com/
    多线程服务器的常用编程模型
    Linux系统shell脚本对字符串、数字、文件的判断
    【2022.01.11】HassOS的备份、HassOS的SSH连接、Docker的部署
    【2022.01.09】了解HassOS的开发者工具——添加自定义组件
    【2022.01.10】装修别墅、洋房户型的一些注意事项
  • 原文地址:https://www.cnblogs.com/DOGame/p/10555985.html
Copyright © 2011-2022 走看看