【网络编程实例】C++实现基于I/O复用select函数的服务器和客户端通信

参考《TCP/IP网络编程第12章》

  • select函数
    在这里插入图片描述
  • 关键宏
    • FD_ZERO(fd_set *fdset) :将 fd_set 变量所指的位全部初始化成0
    • FD_SET(int fd,fd_set *fdset) :在参数 fdset 指向的变量中注册文件描述符 fd 的信息
    • FD_SLR(int fd,fd_set *fdset) :从参数 fdset 指向的变量中清除文件描述符 fd 的信息
    • FD_ISSET(int fd,fd_set *fdset) :若参数 fdset 指向的变量中包含文件描述符 fd 的信息,
      则返回「真」
  • select函数定义
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset,
     fd_set *exceptset, const struct timeval *timeout);
成功时返回大于 0 的值,失败时返回 -1
 maxfd: 监视对象文件描述符数量
 readset: 将所有关注「是否存在待读取数据」的文件描述符注册到 fd_set 型变量,并传递其地址值。
 writeset: 将所有关注「是否可传输无阻塞数据」的文件描述符注册到 fd_set 型变量,并传递其地址值。
 exceptset: 将所有关注「是否发生异常」的文件描述符注册到 fd_set 型变量,并传递其地址值。
 timeout: 调用 select 函数后,为防止陷入无限阻塞的状态,传递超时(time-out)信息
 返回值: 发生错误时返回 -1,超时时返回0,因发生关注的时间返回时,返回大于0的值,该值是发生事件的文件描述符数。
  • 服务器端代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <signal.h>

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;
using std::endl;

#define BUFFSIZE 30
#define DEFAULT_PORT 9190    // 指定端口为 9190

#define LOG_BUFFSIZE 65536

int main()
{
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    struct timeval timeout;
    fd_set reads, cpy_reads;

    socklen_t  adr_sz;
    int fd_max, str_len, fd_num, i;
    char buf[BUFFSIZE];

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    cout << "server socket:" << serv_sock << endl;
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(DEFAULT_PORT);

    //调用 bind 函数分配ip地址和端口号
    if(bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
       cout << "Bind error" << errno << ": " << strerror(errno) << endl;

     //调用 listen 函数将套接字转为可接受连接状态
     if(listen(serv_sock, 5) == -1)
       cout << "Listen error" <<  errno << ": " << strerror(errno) << endl;

    FD_ZERO(&reads);
    FD_SET(serv_sock, &reads); //注册服务端套接字
    fd_max = serv_sock;

    while(1)
    {
        cpy_reads = reads;
        timeout.tv_sec = 5;
        timeout.tv_usec = 5000;

        if((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, &timeout)) == -1)
            break;
        if(fd_num == 0)
            continue;
        for(i = 0; i < fd_max + 1; ++i) 
        {
            if(FD_ISSET(i, &cpy_reads)) //查找发生变化的套接字文件描述符
            {
                if(i == serv_sock)//如果是服务端套接字时,受理连接请求
                {
                    adr_sz = sizeof(clnt_adr);
                    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);

                    FD_SET(clnt_sock, &reads);//注册一个clnt_sock
                    if(fd_max < clnt_sock) fd_max = clnt_sock;
                    cout << "connected client: " << clnt_sock<< endl; 
                }
                else//不是服务端套接字时
                {
                    str_len = read(i, buf, BUFFSIZE); //i指的是当前发起请求的客户端
                    if(str_len == 0)
                    {
                        FD_CLR(i, &reads);
                        close(i);
                        cout << "closed cilent: " << i << endl;
                    }
                    else{
                        write(i, buf, BUFFSIZE);
                    }
                }
            }
        }
    }
    close(serv_sock);
    return 0;
}
  • 客户端代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;
using std::endl;

#define BUFFSIZE 1024
#define SERVER_IP "127.0.0.1"    // 指定服务端的IP
#define SERVER_PORT 9190            // 指定服务端的port


int main()
{
    int sock;
    char message[BUFFSIZE];
    int str_len;
    struct sockaddr_in serv_adr;

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if(sock == -1) cout << "socket() error" << endl;

    bzero(&serv_adr, sizeof(serv_adr));

    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = inet_addr(SERVER_IP);
    serv_adr.sin_port = htons(SERVER_PORT);

    if(connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
        cout << "connect() error: " << errno << " " << strerror(errno) << endl;
    else
        cout << "connected............" << endl;

    while(1)
    {
        cout << "Input message(Q to quit): " << endl;
        cin >> message;
        if(!strcmp(message, "q") || !strcmp(message, "Q"))
            break;

        write(sock, message, strlen(message));
        str_len = read(sock, message, BUFFSIZE - 1);
        // message[str_len] = 0;
        cout << "Message from server:" << message << endl;
    }
    close(sock);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值