此文章源自于EasyMouse项目实践中的设计框架,EasyMouse是一款安卓无线鼠标、键盘软件,可以让你的手机当做鼠标和键盘,远程操作电脑。
欢迎下载使用: EasyMouse官网!
《select多路复用 : EasyMouse的通道模式设计(上)》
监听通道处理OnChannelForTcp
所有新接入的链接都会进行此函数进行处理。此函数只负责建立相应的通道,不负责处理任何数据;
void OnChannelForTcp(Channel* pChl)
{
ASSERT(pChl);
// 新建一个通道
pChannel pNewChl = new Channel();
int addr_len = sizeof(pNewChl->addr);
pNewChl->socket = accept(pChl->socket, &pNewChl->addr, &addr_len);
if (pNewChl->socket < 0){
// something error,
}
pNewChl->type = CHANNEL_TYPE_UNKNOW;// 所有新建连接通道为未识别
pNewChl->pfunc = OnChannelForUnknow;// 未识别通道处理函数
pNewChl->ip = inet_ntoa(pNewChl->addr.sin_addr);
/* 这里你还可以做一些其他的初始化,如接入时间等 */
g_Channel.push_back(pNewChl);// 加入全局通道列表
}
说明:
1, 你看到我们有很多个地方调用g_Channel.push_back(pChl);的方式,其实这里应该封装一些函数专门用来操作g_Channel全局变量,并做好线程互斥。因为这个变量将会有多个线程操作,如设备断开连接的时候,可能需要删除相应的通道;
2, OnChannelForUdp的回调,这里不做介绍。EasyMouse主要是接收广播包,并回复即可;
未知通道处理OnChannelForUnknow
此通道处理函数,主要负责通道类型的识别过程。当一个新的客户端接入的时候,OnChannelForTcp将通过accept立即创建一个未知通道(如上),并且此通道的连接马上进行监听序列,因此理论上这个连接后的数据将立即进入这个通道处理,如登陆认证...
void OnChannelForUnknow(Channel* pChl)
{
// 定义一个你封装的协议包
ProtoPack pack;
if (pack.recv(pChl) <= 0){// 通过你封装的接口,从通道接收一个完整的协议包
// something error, maybe return;
}
/* 这里需要对收到的协议数据做安全监测,避免错误的数据导致程序崩溃 */
switch(pack.service()){// pack.service() 获取链接第一个包的类型
case MAYBE_LOGIN:// 也许是新客户端接入,做登陆认证
// 这里做必要的校验处理,如用户名,密码是否正确,获取终端名,系统等
pChl->type = CHANNEL_TYPE_CMD;// 识别成功,转换为命令通道;
pChl->pfunc = OnChannelForCmd;// 重要
// 发送回包告诉客户端是否登录成功
break;
case MAYBE_SEND_FILE:
// 解析pack.body获取文件名,大小等信息。创建线程来处理此通道
pChl->pid = pthread_create();
pChl->type = CHANNEL_TYPE_DATA;// 识别成功,转换为数据通道;
pChl->pfunc = NULL;
// 发送回包,告诉客户端是否准备好接收文件了。
break;
default:
// something error;
}
}
命令通道处理OnChannelForCmd
这里没有伪代码,你需要根据自己的服务器提供的功能来处理客户端的请求。通过上面的设计模式。当客户端连接服务器,并通过认证之后。接入连接转换为命令通过,客户端所有的服务请求都可以通过这条连接发送请求,并将进入这里进行处理;
说明:
这里主要是接收协议包,处理请求。如果需要响应的话,就往命令通道发送回包即可;
数据通道处理OnChannelForData
数据通道我们是通过线程来处理的。因此不会进行监听序列,你可以看到在服务器主循环逻辑中有排除数据通道if ((*itr)->type != CHANNEL_TYPE_DATA);
数据通道线程处理完文件等类似的数据接收之后,直接关闭数据通道(从全局链表g_Channel删除),所有需要封装一些函数来对g_Channel进行访问,并实现互斥的功能;
数据通道处理线程没接收到一个数据包之后,直接调用此通道的回调函数OnChannelForData处理即可,如保存到文件中;
效果体验
你可以到EasyMouse官网下载此软件体验整个逻辑实现的效果,包括EmServer服务器端,EasyMouse安卓应用。
你可以同时接入多个手机,认证登陆,服务器端和客户端同时互传文件等;