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

转载请注明出处:http://blog.csdn.net/mxway/article/details/44195099

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

一、网络的初始化

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

cfg = new Cfg(nicAddr, portNo = _portNo);
	if ((msgMng = new MsgMng(nicAddr, portNo, cfg))->GetStatus() == FALSE)
	{
		::ExitProcess(0xffffffff);
		return;
	}

在MsgMng类的构造函数中调用WSockInit。

MsgMng::MsgMng(ULONG nicAddr, int portNo, Cfg *_cfg)
{
        ...
	local.addr = nicAddr;
	local.portNo = htons(portNo);
        ...
	if (WSockInit(cfg ? TRUE : FALSE) == FALSE)
		return;
        ...
}
BOOL MsgMng::WSockInit(BOOL recv_flg)
{
	WSADATA		wsaData;
	if (::WSAStartup(0x0101, &wsaData) != 0)
		return	GetSockErrorMsg("WSAStart()"), FALSE;

	if ((udp_sd = ::socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
		return	GetSockErrorMsg("Please setup TCP/IP(controlpanel->network)\r\n"), FALSE;

	if (recv_flg != TRUE)
		return	TRUE;

	if ((tcp_sd = ::socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
		return	GetSockErrorMsg("Please setup2 TCP/IP(controlpanel->network)\r\n"), FALSE;

	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;

	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");
	}

	BOOL	flg = TRUE;	// Non 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;

	int	buf_size = MAX_SOCKBUF, buf_minsize = MAX_SOCKBUF / 2;		// UDP
	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)");

	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)");

	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;

	return	TRUE;
}

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

void TMsgApp::InitWindow(void)
{
        ...
	int			port_no = atoi(cmdLine);

	if (port_no == 0)
		port_no = IPMSG_DEFAULT_PORT;
        ...
        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(调用过程详解见第二篇的消息机制)

BOOL TMainWin::EvCreate(LPARAM lParam)
{
        ...
        msgMng->AsyncSelectRegist(hWnd);
        ...
        if (msgMng->GetStatus())
        EntryHost();
}
BOOL MsgMng::AsyncSelectRegist(HWND hWnd)
{
	if (hAsyncWnd == 0)
		hAsyncWnd = hWnd;

	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;
}

WSAAsyncSelect(udp_sd,hWnd, WM_UDPEVENT,FD_READ),当对udp_sd进行发送数据时,将交由TMainWin的消息处理机制进行处理。由于WM_UDPEVENT是飞鸽传书程序自定义的消息机制所以由TMainWin的EventUser进行处理(具体参照第二篇飞鸽传书的消息机制)。

BOOL TMainWin::EventUser(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        ...
	case WM_UDPEVENT:
		UdpEvent(lParam);
		return	TRUE;
        ...
}

三、用户上线通知

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

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

void TMainWin::EntryHost(void)
{
        ...
	BroadcastEntry(IPMSG_BR_ENTRY);
        ...
}
void TMainWin::BroadcastEntry(ULONG mode)
{
	msgMng->Send((ULONG)~0, htons(portNo), host_status, GetNickNameEx(), cfg->GroupNameStr);
}

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

时间: 2024-11-10 19:25:51

飞鸽传书源码分析三-网络的相关文章

飞鸽传书源码分析五-文件传输

转载请注明出处:http://blog.csdn.net/mxway/article/details/44889871 本文是在飞鸽传书源码v2.06的基础上进行分析的. 1.添加要发送的文件 文件的发送是在发送对话框中进行的,首先找到发送对话框的快捷菜单. File Transfer对应的菜单id为MENU_FILEADD,相应的command处理事件在Senddlg.cpp中的EvCommand函数中 BOOL TSendDlg::EvCommand(WORD wNotifyCode, WO

飞鸽传书源码分析-程序启动过程

转载请注明出处:http://blog.csdn.net/mxway/article/details/39581119 本文章是在飞鸽传书的2.06源码基础上分析 飞鸽传书源码运行流程如下,本篇文章只说明了飞鸽传书的启动过程,对于飞鸽伟书的消息机制及菜单加载等功能都不在本篇文章范围之内. 1. WinMain函数 int WINAPI WinMain(HINSTANCE hI, HINSTANCE, LPSTR cmdLine, int nCmdShow) { TMsgApp app(hI, c

飞鸽传书源码分析二消息机制

转载请注明出处:http://blog.csdn.net/mxway/article/details/40225725 本篇文章是在飞鸽传书2.06源码的基础的分析的. 飞鸽传书的消息大致可分为三类:普通窗口类(后面以TMainWin为例进行分析)消息,对话框类(后面以TSendDlg为例进行分析)消息,对话框控件(后面以TEditSub为例进行分析)消息.这三类消息先合后分,这三类窗口设置的消息处理函数都是TApp::WinProc,在TApp::WinProc函数中再分发给各自的消息处理函数

Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe

Nouveau源码分析(三) 向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照. 对于匹配的设备,PCI模块就调用对应的probe函数,也就是nouveau_drm_probe. // /drivers/gpu/drm/nouveau/nouveau_drm.c 281 static int nouveau_drm_probe(struct pci_dev *pdev, 282 const struct

Hadoop之HDFS原理及文件上传下载源码分析(下)

上篇Hadoop之HDFS原理及文件上传下载源码分析(上)楼主主要介绍了hdfs原理及FileSystem的初始化源码解析, Client如何与NameNode建立RPC通信.本篇将继续介绍hdfs文件上传.下载源解析. 文件上传 先上文件上传的方法调用过程时序图: 其主要执行过程: FileSystem初始化,Client拿到NameNodeRpcServer代理对象,建立与NameNode的RPC通信(楼主上篇已经介绍过了) 调用FileSystem的create()方法,由于实现类为Dis

[Android]Fragment源码分析(三) 事务

Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的非常的出彩.我们先引入一个简单常用的Fragment事务管理代码片段: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); ft.add(R.id.fragmentContainer, fragment, "tag"); ft.addToBackStack("<span style="fo

baksmali和smali源码分析(三)

baksmali 的源码分析 在baksmali进行源码分析之前,需要读者掌握一条主线,因为本身笔者只是由于项目需要用到这套源码,在工作之余的时间里面来进行学习也没有时间和精力熟读源码的每个文件每个方法,但是依据这条主线,至少能够猜出并且猜对baksmali里面的源码的文件大概的作用是什么,这样在修改问题和移植的时候才能做到游刃有余. 这条主线是,baksmali其实只是利用了dexlib2提供的接口,将dex文件读入到一块内存中,这块内存或者说数据结构开辟的大小是跟输入的dex文件相关的,而这

横屏小游戏--萝莉快跑源码分析三

主角出场: 初始化主角 hero = new GameObjHero(); hero->setScale(0.5); hero->setPosition(ccp(100,160)); hero->setVisible(false); addChild(hero,1); 进入GameObjHero类ccp文件 创建主角及动作 this->setContentSize(CCSizeMake(85,90)); //接收触摸事件 CCDirector* pDirector = CCDire

哇!板球 源码分析三

守门员出场 守门员出场,每个守门员是从屏幕的右侧中间的位置随机方向向左侧移动 FielderSprite* fielderSprite1 = FielderSprite::create("pic/fielder.png"); //守门员精灵初始位置为右侧中间位置 fielderSprite1->setPosition(ccp(GOALKEEPER_X, GOALKEEPER_Y)); fielderSprite1->setAnchorPoint(ccp(0.5, 0.5))