MFC基于select模型的套接字类之服务器(6)

(2)套接字的Select模型

Select模型是套接字中最常见的模型。它的核心是利用select()函数实现套接字的输入输出管理。利用select()函数,应用程序可以判断指定套接字上是否存在数据,如果套接字上存在数据,则调用recv()函数进行接收;还可以通过该函数判断能否向指定套接字上发送数据,即指定套接字是否已经准备好接收数据,如果已经准备好,则调用send()函数发送数据。

在使用Select模型时,需要用到fd_set结构以及FD_ZEROFD_SET等宏。

fd_set是一个管理多个套接字的结构体。该结构体的定义为

typedef struct fd_set{
   u_int fd_count;
   SOCKET fd_array[FD_SETSIZE];
}fd_set;
其中,fd_count 表示管理的套接字数量; fd_array SOCKET 类型的数组,其元素是管理的套接字, FD_SETSIZE 的值是 64 ,也就是说最多可以管理 64 个套接字。在前文提到的 select() 函数就是对 fd_set 类型的变量进行管理。

FD_ZERO宏:该宏的作用是初始化fd_set结构的对象;

FD_SET宏:该宏的作用是将指定的套接字加入到fd_set结构的对象中;

(2)Select模型的实现

ThreadFunc_RecvData()函数中,首先定义fd_set结构的对象,用来保存要管理的套接字:

fd_set socket_fs;

接下来通过while()语句,循环接收来自客户端的数据。

int socketindex = pServer->m_client_currentindex;
while (pServer->m_clientconnectflag_array[socketindex])
{
          ........
}
其中,pServer ThreadFunc_RecvData() 函数的参数,使用方法在“ 2.3.3 定义线程函数”中已介绍。通过 pServer 可以在CTCPSocket_Server 类的静态函数中调用该类的普通成员。m_client_currentindex 是套接字在“套接字池”中的索引,该索引值在“ 2.3.2 指定回调函数”提到的代码中获取。将 m_client_currentindex 保存在变量 socketindex 中的原因是,在接受客户端连接的线程 ThreadFunc_StartServer 中,如果有新的客户端连入服务端, m_client_currentindex 值会发生改变。如果不将该值保存在临时变量中,在接收客户端数据时可能会发生错误。

while()的循环语句是数组pServer->m_clientconnectflag_array中的元素。在“2.3.3 定义线程函数”中提到,该数组中包含的是“套接字池”中的套接字是否可用的标志。也就是说,当指定的套接字可用时,while()循环的语句才被执行。当将相应的元素值设置为false时,将结束接收数据的线程。

while()循环中,首先调用FD_ZERO宏清空套接字集合socket_fs,之后将与客户端通信的套接字添加到该集合中。

FD_ZERO(&socket_fs);
FD_SET(pServer->m_clientsocket_array[socketindex], &socket_fs);
FD_SET() 宏的第一个参数是要加入集合的套接字,第二个参数是套接字集合的指针。之后,利用 select() 函数判断套接字集合中的套接字是否可读、可写。 select() 函数的格式为

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

其中,参数nfds 为保留参数,可以将其设置为 0 readfds 是具有可读性套接字集合的指针,如果要判断套接字集合中的套接字中是否有数据,则要设置该参数; writefds 是具有可写性套接字集合的指针,如果要判断套接字集合中的套接字是否已经准备好接收数据,则要设置该参数; exceptfds 是检查错误套接字集合的指针,如果要检查套接字集合中的套接字是否发生错误,则要设置该参数; timeout 用于设置调用 select() 函数时的等待时间,其类型是 timeval 结构,该结构主要指定时间,其格式为

typedef struct timeval{
long tv_sec;
long tv_usec;
}timeval;
tv_sec 表示秒, tv_usec 表示毫秒,在使用时指定两个成员变量中的一个即可。 select() 函数的返回值是套接字集合中发生可读、可写或者异常的套接字数量,如果是 0 则表示已经过了等待时间还没有发生可读、可写或者异常。

if (select(0, &socket_fs, NULL, NULL, &time_selectwait) == 1)
{
value_recvretrun = recv(pServer->m_clientsocket_array[socketindex], buf, LENGTH_RECVDATA, 0);
if (pServer->m_receiveclientdata_proc != NULL)
{
pServer->m_receiveclientdata_proc(buf, LENGTH_RECVDATA, socketindex);
}
}
其中,time_selectwait timeval 结构的对象,其定义为

timeval time_selectwait = { 0, 5000 };
select() 函数等待 5 秒钟后,如果在套接字集 socket_fs 中没有套接字变为可读,则函数返回 0 ,此时通过 while() 语句,继续调用 select() 函数等待套接字变为可读,即 select() 函数返回值是 1 。当套接字变为可读,则说明套接字中有数据等待读取,此时调用 recv() 函数接收数据。

(3)接收数据

服务端通过recv()函数,通过套接字接收来自客户端的数据。该函数的格式是

int recv(SOCKET s, char* buf, int len, int flags);
其中,参数s 表示指定的套接字; buf 指定了保存数据的缓冲区; len 表示缓冲区 buf 的长度; flags 参数会影响 recv() 函数的行为,如果将该参数设置为 0 ,则表示没有特殊行为。如果成功接收到了来自客户端的数据,则 recv() 函数的返回值为接收到数据的大小。

在接收到了客户端数据之后,调用2.3.2 指定回调函数”中提到的回调函数m_receiveclientdata_proc,将接收到的数据交给主窗口来处理。

(4)更新“套接字池”

当客户端退出时,需要对服务端的“套接字池”进行更新。客户端的退出分为两种情况:“优雅”退出和“强行”退出。“优雅”退出指的是客户端调用了closesocket()函数关闭套接字,“强行”退出指的是直接关闭了客户端程序。recv()函数通过不同的返回值来反映这两种退出。当客户端“优雅”退出时,recv()函数的返回值是0;当客户端“强行”退出时,recv()函数的返回值是-1,即SOCKET_ERROR。所以,在“(3Select模型的实现”中提到的while()循环的执行代码中,还需添加对recv()函数返回值的判断:

if (SOCKET_ERROR == value_recvretrun || 0 == value_recvretrun)
{
closesocket(pServer->m_clientsocket_array[socketindex]);
pServer->m_clientconnectflag_array[socketindex] = false;
}
当客户端退出时,服务端通过closesocket() 函数关闭与客户端通信的套接字,并且把“套接字池”的客户端套机字是否合法的数组进行更新,将该数组的相应元素设置为 false 。此时, while() 循环会退出。在循环退出之后,对“套接字池”的客户端当前索引值进行更新

pServer->m_client_currentindex--;


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于MFC的C++的select模型的TCP聊天室 C++是一种广泛使用的编程语言,它是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++是C语言的扩展,旨在提供更强大的编程能力,包括面向对象编程和泛型编程的支持。C++支持数据封装、继承和多态等面向对象编程的特性和泛型编程的模板,以及丰富的标准库,提供了大量的数据结构和算法,极大地提高了开发效率。12 C++是一种静态型的、编译式的、通用的、大小写敏感的编程语言,它综合了高级语言和低级语言的特点。C++的语法与C语言非常相似,但增加了许多面向对象编程的特性,如、对象、封装、继承和多态等。这使得C++既保持了C语言的低级特性,如直接访问硬件的能力,又提供了高级语言的特性,如数据封装和代码重用。13 C++的应用领域非常广泛,包括但不限于教育、系统开发、游戏开发、嵌入式系统、工业和商业应用、科研和高性能计算等领域。在教育领域,C++因其结构化和面向对象的特性,常被选为计算机科学和工程专业的入门编程语言。在系统开发领域,C++因其高效性和灵活性,经常被作为开发语言。游戏开发领域中,C++由于其高效性和广泛应用,在开发高性能游戏和游戏引擎中扮演着重要角色。在嵌入式系统领域,C++的高效和灵活性使其成为理想选择。此外,C++还广泛应用于桌面应用、Web浏览器、操作系统、编译器、媒体应用程序、数据库引擎、医疗工程和机器人等领域。16 学习C++的关键是理解其核心概念和编程风格,而不是过于深入技术细节。C++支持多种编程风格,每种风格都能有效地保证运行时间效率和空间效率。因此,无论是初学者还是经验丰富的程序员,都可以通过C++来设计和实现新系统或维护旧系统。3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值