SocketHandle类分析
类用途:
负责select调用,管理多个SOCKET,封装FD_SET,FD_ISSET之类的操作,调用Socket函数中定义的回调函数。新加线程安全.
可以说,此类是执行官。别的类做具体工作,它负责调配。
基础:
下面要用到的结构:
typedef std::list socket_v;
从定义来看,是一SOCKET的集合,分开保存一些不同类型的SOCKET,如可读的,可写的。
typedef std::map socket_m;
这是原始SOCKET和Socket *(类指针)的组合,用map类最合适.
对上述两种结构C++标准程序库里有详细介绍.
注: 偶只对Select()方法中用到的一些熟悉,有些如socks4协议之类的和线程安全有关的东东还没弄透。这儿把偶用过的写下来。
一些保护变量:
protected:
socket_m m_sockets; ///< Active sockets list
socket_m m_add; ///< Sockets to be added to sockets list
std::list m_delete; ///< Sockets to be deleted (failed when Add)
一些私有变量:
private:
StdLog *m_stdlog; ///< Registered log class, or NULL
SOCKET m_maxsock; ///< Highest file descriptor + 1 in active sockets list
fd_set m_rfds; ///< file descriptor set monitored for read events
fd_set m_wfds; ///< file descriptor set monitored for write events
fd_set m_efds; ///眼熟吧。这三个变量和select模式中的是一样的。
#ifdef _WIN32
int m_preverror; ///< debug select() error
#endif
bool m_slave; ///< Indicates that this is a SocketHandler run in SocketThread
//下面带socks4字样的用于socks4协议,具体上cnpaf.net去看
ipaddr_t m_socks4_host; ///< Socks4 server host ip
port_t m_socks4_port; ///< Socks4 server port number
std::string m_socks4_userid; ///< Socks4 userid
bool m_bTryDirect; ///< Try direct connection if socks4 server fails
//
int m_resolv_id; ///< Resolver id counter
ResolvServer *m_resolver; ///< Resolver thread pointer
port_t m_resolver_port; ///< Resolver listen port
bool m_b_enable_pool; ///< Connection pool enabled if true
//下面几个老外写的清楚,怕偶翻译了反而有人不明白,不过SOCKET多了,象这样分开保存也是必要的。
socket_v m_fds; ///< Active file descriptor list
socket_v m_fds_erase; ///< File descriptors that are to be erased from m_sockets
socket_v m_fds_callonconnect; ///< checklist CallOnConnect
socket_v m_fds_detach; ///< checklist Detach
socket_v m_fds_connecting; ///< checklist Connecting
socket_v m_fds_retry; ///< checklist retry client connect
socket_v m_fds_close; ///< checklist close and delete
//互斥体,用于多线程,线程安全,不过偶还没用到。用到了再来补充。
Mutex& m_mutex; ///< Thread safety mutex
bool m_b_use_mutex; ///< Mutex correctly initialized
最关键的函数是Select(),共有三个,其实只有
int SocketHandler::Select(struct timeval *tsel)
这一个是真正执行的.
真正的Select代码分析已在中写过.
在<之一>上main里的函数有通用性.
首选声明一个SocketHandler对象,如h,再声明一个继承自Socket类的对象(如TcpSocket,一般是自定义类,重载方法,可以从TcpSocket,HttpGetSocket,HttpSocket继承,当然直接从Socket类继承也可以,不过TcpSocket类的好处就享受不到了^_^),假设是mysocket,下一步可以进行下面的方法了:
h.add(&mysocket);
最后,写个select循环,就可以了.循环结束,不出错的话,我们所需要的都完成了.
先说说m_sockets,m_add,m_delete这三个结构
先看add方法,用到了m_add,m_delete.
add方法一般是必用的(在TcpSocket级别上而不是HttpGetSocket级别上)
void SocketHandler::Add(Socket *p)
{
if (p -> GetSocket() == INVALID_SOCKET)
{ //如果SOCKET无效,m_delete会保存这个类指针
if (p -> CloseAndDelete())
{
m_delete.push_back(p);
}
return;
}
m_add[p -> GetSocket()] = p; //把包含有效SOCKET的Socket类指针保存在m_add里
}
注意到Select函数开头有一段:
while (m_add.size())
{
socket_m::iterator it = m_add.begin();
SOCKET s = (*it).first;
Socket *p = (*it).second;
//省略若干验证,处理......
m_fds.push_back(s); //这句在下面m_fds处有说明
m_sockets[s] = p; //此处m_sockets增加元素
m_add.erase(it) //而m_add相应的删除这个
}
说明m_add 与m_sockets有一定的关系.
对于一个已初始化的类来说,先加入m_add容器,Select调用时,检查是否在m_sockets容器中存在,如不存在,则加入m_sockets容器.无论存在不存在,都将从m_add容器中删除此类指针.
下面看看变量 m_fds,类似的有几个,以这一个为例.
在上面的Select调用中,有一句:
m_fds.push_back(s);
只在Select调用的开始处有这么一句.所有的可用SOCKET都将保存到这个容器中.下面的FD_ISET还要对这个容器的元素进行论循.所以这是个很重要的结构.
m_fds_*这类的还有几个,分别包含不同类别的SOCKET,用于其他的用途.举一例:
if( m_fds_callonconnect.size() )
{
//在这里会对m_fds_callonconnect的元素论循.对每一个SOCKET 进行如下操作
/****************开始*****************************************
调用Socket::SetConnected();设定已连接状态为真
如果此SOCKET所在Socket类缓冲区有数据,则调用TcpSocket::OnWrite发送数据
如果重连接标志已设立,调用Socket::OnReconnect(),否则调用Socket::OnConnect(),这都是定义好的回调函数,(用虚函数实现的)
*************************************************************/
}
再重复一下,这个
Socket::OnConnect()
对于使用者来说只需重载它,看偶<之一>的代码是调用了SendBuf(),发出自己的GET请求.
待续