本次主要分析通信部分:
Msgmng.cpp为通信处理部分的源文件,其中包括了几乎所有的socket通信代码。
Class Msgmng为通信的管理类。构造函数如下:
Line: 18
MsgMng::MsgMng(ULONG nicAddr, int portNo, Cfg *_cfg){}
此函数主要功能如下:
1、对数据成员进行初始化赋值;
2、调用WSockInit函数初始化sock,此初始化函数在下面分析。此函数设置了socket的属性,比较重要。
3、获取主机名称、获取当前用户名,并且填充信息结构。
BOOL MsgMng::WSockInit(BOOL recv_flg)
函数为初始化文件。其中实现的主要功能如下:
- //Line:67
- if ((udp_sd = ::socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
- return GetSockErrorMsg("Please setup TCP/IP(controlpanel->network)/r/n"),FALSE;
//创建基于数据报的socket udp_sd;
- if ((tcp_sd = ::socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
- return GetSockErrorMsg("Please setup2 TCP/IP(controlpanel->network)/r/n"), FALSE;
- //创建基于流的 socket tcp_sd;
创建本地地址结构,本地地址和本地端口均保存在local中,local为自己定义的一个HostSub类型的数据,HostSub类型的数据保存用户名称,主机名称、主机端口、主机地址等信息。
- //Line: 76
- struct sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = local.addr;
- addr.sin_port = local.portNo;
- //Line:82
- if (::bind(udp_sd, (LPSOCKADDR)&addr, sizeof(addr)) != 0)
- return GetSockErrorMsg("bind()"), FALSE;
- if (::bind(tcp_sd, (LPSOCKADDR)&addr, sizeof(addr)) != 0)
- {
- ::closesocket(tcp_sd);
- tcp_sd = INVALID_SOCKET;
- GetSockErrorMsg("bind(tcp) error. Can't support file attach");
- }
分别绑定套接字和本机地址,由于是用的不同通信协议(udp、tcp),故相同的地址和端口可以绑定两次。
- //Line : 92
- BOOL flg = TRUE; //none block
- if (::ioctlsocket(udp_sd, FIONBIO, (unsigned long *)&flg) != 0)
- return GetSockErrorMsg("ioctlsocket(nonblock)"), FALSE;
- if (IsAvailableTCP() && ::ioctlsocket(tcp_sd, FIONBIO, (unsigned long *)&flg) != 0)
- return GetSockErrorMsg("ioctlsocket tcp(nonblock)"), FALSE;
- flg = TRUE; // allow broadcast
- if (::setsockopt(udp_sd, SOL_SOCKET, SO_BROADCAST, (char *)&flg, sizeof(flg)) != 0)
- return GetSockErrorMsg("setsockopt(broadcast)"), FALSE;
- //以上部分设置两个套接字的套接字选项,设置udp为非阻塞模式,tcp_sd允许发送广播信息。
- Line :103
- int buf_size = MAX_SOCKBUF, buf_minsize = MAX_SOCKBUF / 2;
- if (::setsockopt(udp_sd, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof(int)) != 0
- && ::setsockopt(udp_sd, SOL_SOCKET, SO_SNDBUF, (char *)&buf_minsize, sizeof(int)) != 0)
- GetSockErrorMsg("setsockopt(sendbuf)");
- //SOCKET,SNDBUF用于设置 发送缓冲区的size
- //Sets the per-socket buffer size for sending data
- //Line: 108
- buf_size = MAX_SOCKBUF, buf_minsize = MAX_SOCKBUF / 2;
- if (::setsockopt(udp_sd, SOL_SOCKET, SO_RCVBUF, (char *)&buf_size, sizeof(int)) != 0
- && ::setsockopt(udp_sd, SOL_SOCKET, SO_RCVBUF, (char *)&buf_minsize, sizeof(int)) != 0)
- GetSockErrorMsg("setsockopt(recvbuf)");
- //同上,设置接受缓冲区的size
- //Line : 113
- flg = TRUE; // REUSE ADDR
- if (IsAvailableTCP() && ::setsockopt(tcp_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&flg, sizeof(flg)) != 0)
- GetSockErrorMsg("setsockopt tcp(reuseaddr)");
- if (IsAvailableTCP() && ::listen(tcp_sd, 5) != 0)
- return FALSE;
- 设置地址重用,并且开始进行监听,看监听函数在这里。
以下函数为MsgMng的Send函数,函数有三个原型,分别如下:
参数2传递地址,参数2, 参数3传递将要传送的数值。在程序处理中,将int类型的数值转换成char型,调用重载函数2进行发送。
- 1)BOOL MsgMng::Send(HostSub *hostSub, ULONG command, int val)
- {
- char buf[MAX_NAMEBUF];
- wsprintf(buf, "%d", val);
- return Send(hostSub->addr, hostSub->portNo, command, buf);
- }
- //函数2调用函数3进行发送。
- 2)BOOL MsgMng::Send(HostSub *hostSub, ULONG command, const char *message, const char *exMsg)
- {
- return Send(hostSub->addr, hostSub->portNo, command, message, exMsg);
- }
- //函数调用MakeMsg函数根据参数生成不同的消息类型,此消息类型为自己定义的,此编程思想值得借鉴。
- 3)BOOL MsgMng::Send(ULONG host, int port_no, ULONG command, const char *message, const char *exMsg)
- {
- char buf[MAX_UDPBUF];
- int trans_len;
- MakeMsg(buf, command, message, exMsg, &trans_len);
- return UdpSend(host, port_no, buf, trans_len);
- }
MakeMsg函数完成消息的组装。函数有两个重载版本,主要功能从函数2实现。
- 1)ULONG MakeMsg(char *udp_msg, ULONG command, const char *msg, const char *exMsg=NULL, int *packet_len=NULL)
- {
- return MakeMsg(udp_msg, MakePacketNo(), command, msg, exMsg, packet_len);
- }
- 函数一创建分包号,然后调用函数二。
- 2)ULONG MsgMng::MakeMsg(char *buf, int _packetNo, ULONG command, const char *msg, const char *exMsg, int *packet_len)
- {
- int len, ex_len = exMsg ? strlen(exMsg) + 1 : 0, max_len = MAX_UDPBUF;
- if (packet_len == NULL)
- packet_len = &len;
- *packet_len = wsprintf(buf, "%d:%ld:%s:%s:%ld:", IPMSG_VERSION, _packetNo, local.userName, local.hostName, command);
- if (ex_len + *packet_len + 1 >= MAX_UDPBUF)
- ex_len = 0;
- max_len -= ex_len;
- if (msg != NULL) // sprintf偼巊傢側偄
- // 此处为指针后移,使当前buf指针指向前面已经编制好的消息(v、no、username、hostname)等信息的后面。!!!!localNettoUnix主要完成除去字符串中的换行字符的目的 'r'
- *packet_len += LocalNewLineToUnix(msg, buf + *packet_len, max_len - *packet_len);
- (*packet_len)++;
- // 将扩展信息添加到消息包中。
- if (ex_len)
- {
- memcpy(buf + *packet_len, exMsg, ex_len);
- *packet_len += ex_len;
- }
- return _packetNo;
- }
下面分析 udpsend函数,此函数完成主要的数据发送任务。
- 2)BOOL MsgMng::UdpSend(ULONG host_addr, int port_no, const char *buf, int len)
- {
- struct sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = port_no;
- addr.sin_addr.s_addr = host_addr;
- if (::sendto(udp_sd, buf, len, 0, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
- {
- switch (WSAGetLastError()) {
- case WSAENETDOWN:
- break;
- case WSAEHOSTUNREACH:
- static BOOL done;
- if (done == FALSE) {
- done = TRUE;
- }
- return FALSE;
- default:
- return FALSE;
- }
- // 重置socket
- if (WSockReset() != TRUE)
- return FALSE;
- // 注册此窗口为异步选择窗口。在对socket消息的处理中应用了异步选择机制,使socket消息关联到特定的消息窗口。
- if (hAsyncWnd && AsyncSelectRegist(hAsyncWnd) != TRUE)
- return FALSE;
- // 尝试重新发送
- if (::sendto(udp_sd, buf, len, 0, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
- return FALSE;
- }
- return TRUE;
- }
- Line:172
- BOOL MsgMng::AsyncSelectRegist(HWND hWnd)
- {
- if (hAsyncWnd == 0)
- hAsyncWnd = hWnd;
- // 用自定义的消息(特殊时间发生)通知特定的窗口处理socket消息
- if (::WSAAsyncSelect(udp_sd, hWnd, WM_UDPEVENT, FD_READ) == SOCKET_ERROR)
- return FALSE;
- if (::WSAAsyncSelect(tcp_sd, hWnd, WM_TCPEVENT, FD_ACCEPT|FD_CLOSE) == SOCKET_ERROR)
- return FALSE;
- return TRUE;
- }
未完待续...