飞鸽传书源码分析三-网络

http://blog.csdn.net/mxway/article/details/44195099


本文是在飞鸽传书2.06源码基础之上进行分析的。

一、网络的初始化

      飞鸽传书主窗口对应的类TMainWin的构造函数中有如下的代码

  1. cfg = new Cfg(nicAddr, portNo = _portNo);  
  2.     if ((msgMng = new MsgMng(nicAddr, portNo, cfg))->GetStatus() == FALSE)  
  3.     {  
  4.         ::ExitProcess(0xffffffff);  
  5.         return;  
  6.     }  

在MsgMng类的构造函数中调用WSockInit。
  1. MsgMng::MsgMng(ULONG nicAddr, int portNo, Cfg *_cfg)  
  2. {  
  3.         ...  
  4.     local.addr = nicAddr;  
  5.     local.portNo = htons(portNo);  
  6.         ...  
  7.     if (WSockInit(cfg ? TRUE : FALSE) == FALSE)  
  8.         return;  
  9.         ...  
  10. }  
  1. BOOL MsgMng::WSockInit(BOOL recv_flg)  
  2. {  
  3.     WSADATA     wsaData;  
  4.     if (::WSAStartup(0x0101, &wsaData) != 0)  
  5.         return  GetSockErrorMsg("WSAStart()"), FALSE;  
  6.   
  7.     if ((udp_sd = ::socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)  
  8.         return  GetSockErrorMsg("Please setup TCP/IP(controlpanel->network)\r\n"), FALSE;  
  9.   
  10.     if (recv_flg != TRUE)  
  11.         return  TRUE;  
  12.   
  13.     if ((tcp_sd = ::socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)  
  14.         return  GetSockErrorMsg("Please setup2 TCP/IP(controlpanel->network)\r\n"), FALSE;  
  15.   
  16.     struct sockaddr_in  addr;  
  17.     memset(&addr, 0, sizeof(addr));  
  18.     addr.sin_family         = AF_INET;  
  19.     addr.sin_addr.s_addr    = local.addr;  
  20.     addr.sin_port           = local.portNo;  
  21.   
  22.     if (::bind(udp_sd, (LPSOCKADDR)&addr, sizeof(addr)) != 0)  
  23.         return  GetSockErrorMsg("bind()"), FALSE;  
  24.   
  25.     if (::bind(tcp_sd, (LPSOCKADDR)&addr, sizeof(addr)) != 0)  
  26.     {  
  27.         ::closesocket(tcp_sd);  
  28.         tcp_sd = INVALID_SOCKET;  
  29.         GetSockErrorMsg("bind(tcp) error. Can't support file attach");  
  30.     }  
  31.   
  32.     BOOL    flg = TRUE; // Non Block  
  33.     if (::ioctlsocket(udp_sd, FIONBIO, (unsigned long *)&flg) != 0)  
  34.         return  GetSockErrorMsg("ioctlsocket(nonblock)"), FALSE;  
  35.   
  36.     if (IsAvailableTCP() && ::ioctlsocket(tcp_sd, FIONBIO, (unsigned long *)&flg) != 0)  
  37.         return  GetSockErrorMsg("ioctlsocket tcp(nonblock)"), FALSE;  
  38.   
  39.     flg = TRUE;         // allow broadcast  
  40.     if (::setsockopt(udp_sd, SOL_SOCKET, SO_BROADCAST, (char *)&flg, sizeof(flg)) != 0)  
  41.         return  GetSockErrorMsg("setsockopt(broadcast)"), FALSE;  
  42.   
  43.     int buf_size = MAX_SOCKBUF, buf_minsize = MAX_SOCKBUF / 2;      // UDP   
  44.     if (::setsockopt(udp_sd, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof(int)) != 0  
  45.     &&  ::setsockopt(udp_sd, SOL_SOCKET, SO_SNDBUF, (char *)&buf_minsize, sizeof(int)) != 0)  
  46.         GetSockErrorMsg("setsockopt(sendbuf)");  
  47.   
  48.     buf_size = MAX_SOCKBUF, buf_minsize = MAX_SOCKBUF / 2;  
  49.     if (::setsockopt(udp_sd, SOL_SOCKET, SO_RCVBUF, (char *)&buf_size, sizeof(int)) != 0  
  50.     &&  ::setsockopt(udp_sd, SOL_SOCKET, SO_RCVBUF, (char *)&buf_minsize, sizeof(int)) != 0)  
  51.         GetSockErrorMsg("setsockopt(recvbuf)");  
  52.   
  53.     flg = TRUE; // REUSE ADDR  
  54.     if (IsAvailableTCP() && ::setsockopt(tcp_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&flg, sizeof(flg)) != 0)  
  55.         GetSockErrorMsg("setsockopt tcp(reuseaddr)");  
  56.   
  57.     if (IsAvailableTCP() && ::listen(tcp_sd, 5) != 0)  
  58.         return  FALSE;  
  59.   
  60.     return  TRUE;  
  61. }  

在WSockInit中初始化进行网络编程所要用的dll,并设置了tcp服务及udp所占用的端口号,并设置udp及tcp的socket为非阻塞模式。tcp及udp服务所占的端口是由TMainWin的构造函数传到MsgMng的构造函数中的。在TMainWin的构造函数传给MsgMng的端号来自TMainWin的构造函数。回到Ipmsg.cpp的TMsgApp::InitWindow函数中(具体可参考前面的飞鸽传书源码分析文章)

  1. void TMsgApp::InitWindow(void)  
  2. {  
  3.         ...  
  4.     int         port_no = atoi(cmdLine);  
  5.   
  6.     if (port_no == 0)  
  7.         port_no = IPMSG_DEFAULT_PORT;  
  8.         ...  
  9.         mainWnd = new TMainWin(nicAddr, port_no);mainWnd->Create(class_name, IP_MSG, WS_OVERLAPPEDWINDOW | (IsNewShell() ? WS_MINIMIZE : 0));...}  
在InitWindow函数可以看到所使用端口的初始化,cmdLine是指以命令行方式运行飞鸽传书时参的端口参数,为了简化问题不考虑命令运行。port_no被初始化为IPMSG_DEFAULT_PORT,IPMSG_DEFAULT_PORT是一个宏定义

#define IPMSG_DEFAULT_PORT 0x0979

飞鸽传书默认服务的端口是十六进制979也就是十进制的2425。

二、网络非阻塞模式

在windows下实现socket有多种方式,飞鸽传书使用的是将网络事件以消息方式发送给窗口句柄,而处理网络事件的窗口就是TMainWin。在IPmsg.cpp的InitWindow调用完mainWnd->Create(class_name, IP_MSG, WS_OVERLAPPEDWINDOW | (IsNewShell() ? WS_MINIMIZE : 0));后会执行TMainWin的EvCreate(调用过程详解见第二篇的消息机制)

  1. BOOL TMainWin::EvCreate(LPARAM lParam)  
  2. {  
  3.         ...  
  4.         msgMng->AsyncSelectRegist(hWnd);  
  5.         ...  
  6.         if (msgMng->GetStatus())  
  7.         EntryHost();  
  8. }  
  9. BOOL MsgMng::AsyncSelectRegist(HWND hWnd)  
  10. {  
  11.     if (hAsyncWnd == 0)  
  12.         hAsyncWnd = hWnd;  
  13.   
  14.     if (::WSAAsyncSelect(udp_sd, hWnd, WM_UDPEVENT, FD_READ) == SOCKET_ERROR)  
  15.         return  FALSE;  
  16.   
  17.     if (::WSAAsyncSelect(tcp_sd, hWnd, WM_TCPEVENT, FD_ACCEPT|FD_CLOSE) == SOCKET_ERROR)  
  18.         return  FALSE;  
  19.   
  20.     return  TRUE;  
  21. }  
WSAAsyncSelect(udp_sd,hWnd, WM_UDPEVENT,FD_READ),当对udp_sd进行发送数据时,将交由TMainWin的消息处理机制进行处理。由于WM_UDPEVENT是飞鸽传书程序自定义的消息机制所以由TMainWin的EventUser进行处理(具体参照第二篇飞鸽传书的消息机制)。
  1. BOOL TMainWin::EventUser(UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.         ...  
  4.     case WM_UDPEVENT:  
  5.         UdpEvent(lParam);  
  6.         return  TRUE;  
  7.         ...  
  8. }  
三、用户上线通知

飞鸽传书是工作是局域网的程序,当有新的设备使用飞鸽传书程序时,会自动通知到其它已经在线的用户,将自己显示到其它用户的列表中。

调用完msgMng->AsyncSelectRegist(hWnd);将网络事件与窗口句柄勾搭上之后,程序就会对局域网进行广播,告诉其它人自己打开了飞鸽传书程序。

  1. void TMainWin::EntryHost(void)  
  2. {  
  3.         ...  
  4.     BroadcastEntry(IPMSG_BR_ENTRY);  
  5.         ...  
  6. }  
  1. void TMainWin::BroadcastEntry(ULONG mode)  
  2. {  
  3.     msgMng->Send((ULONG)~0, htons(portNo), host_status, GetNickNameEx(), cfg->GroupNameStr);  
  4. }  
msgMng->Send((ULONG)~0, htons(portNo), host_status, GetNickNameEx(), cfg->GroupNameStr);向局域网发送广播消息,告诉别人自己上线。其它正在运行的程序收到这个消息后就将收到的信息存储起来,以备后面使用,具体过程不再详解。关于飞鸽传书发送消息的格式后面的文章再介绍。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值