当前位置: 首页 > 工具软件 > IPMsg4Android > 使用案例 >

IPMSG(飞秋)源代码调试笔记

董意蕴
2023-12-01

1、怎么发送普通消息?

进行socket异步select选择,每个消息都是一个Msg包,并且分配一个senddlg为其发送消息。消息协议可以参考其他文档,消息统一封装成文本的方式发送,封装方式为[属性1:属性2:...:消息扩展消息],属性和属性之间和消息之间用冒号隔开,消息和扩展消息用消息结构体的exOffset标记隔开,发送的内容可以协定解密方式。

2、使用udp协议发送,如何确定消息是否已经发送成功?

消息发送出去,对方收到消息后会返回一条消息接收成功的确认消息,

然后发送端为每个senddlg开启一个ontimer函数,每1500ms确定一次(时间可以自己配置)发送出去的消息是否收到的回应,如果未收到则重新发送。

3、接收端如何确定两个消息是一样的,而丢弃后面的?

判断packetNo,判断addr ,判断 portNo,pocketNo是根据time()生成的。

4、msg 结构体(如下)的exOffset字段干啥用的?

struct MsgBuf {
	HostSub	hostSub;
	int		version;
	int		portNo;
	ULONG	packetNo;
	ULONG	command;
	int		exOffset;	// expand message offset in msgBuf
	int		dummy;
	char	msgBuf[MAX_UDPBUF];

	void	Init(MsgBuf *org) {
		if (org == NULL) {
			memset(this, 0, (char *)&this->dummy - (char *)this);
			*msgBuf = 0;
			return;
		}
		memcpy(this, org, (char *)&this->dummy - (char *)this);
		strcpy(this->msgBuf, org->msgBuf);
		strcpy(this->msgBuf + exOffset, org->msgBuf + exOffset);
	}
};

    exOffset表示可以发送除消息意外的其他内容(比如组名称啥的,和消息区别开来),但前提是整个消息(加上exOffset)大小要小于MAX_UDPBUF。其实不用这个字段也行,我们可以把组名称啥的封装到消息buf里面去

5、如果输入的字符串超出了MAX_UDPBUF怎么办?

飞秋里面没处理,直接截断。。。

可以查看如下两处处理方式:

(1)从编辑框获取文本的时候,最大长度就用MAX_UDPBUF:

GetDlgItemText(SEND_EDIT, msg.msgBuf, MAX_UDPBUF);

(2)收取消息的时候,还会进行如下代码判断:

if (exStr && (ex_len = strlen(exStr) + 1) < MAX_UDPBUF -1)

{

if (++msg->exOffset + ex_len >= MAX_UDPBUF)

msg->msgBuf[(msg->exOffset = MAX_UDPBUF - ex_len) -1] = '\0';//这句表示超出直接截断

memcpy(msg->msgBuf + msg->exOffset, exStr, ex_len);

}

6、在创建IPM的时候就进入了tcp_socket 就进入了listen的状态。然后udp和tcp还可以同时使用一个端口,包括登入退出发送的广播消息一并使用同一个端口,UDP发送消息,TCP发送文件,对socket的异步管理比较到位。

7、文件发送如何高效?

(1)使用内存映射文件的方式打开文件,内存映射文件方式能高效处理大文件。

(2)为发送和接收程序单独开线程,并使用select选择模型处理数据。

用内存映射方式打开关闭文件:

BOOL TMainWin::OpenSendFile(const char *fname, SendFileObj *obj)
{
	DWORD	lowSize, highSize, viewSize;

	if ((obj->hFile = ::CreateFile(fname, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) != INVALID_HANDLE_VALUE)
	{
		lowSize = ::GetFileSize(obj->hFile, &highSize);
		if ((obj->fileSize = (_int64)highSize << 32 | lowSize) == 0)
			return	TRUE;
		obj->hMap = ::CreateFileMapping(obj->hFile, 0, PAGE_READONLY, highSize, lowSize, 0);
		viewSize = (int)(obj->fileSize > cfg->ViewMax ? cfg->ViewMax : obj->fileSize);
		highSize = (int)(obj->offset >> 32);
		lowSize = (int)((obj->offset / cfg->ViewMax) * cfg->ViewMax);
		obj->mapAddr = (char *)::MapViewOfFile(obj->hMap, FILE_MAP_READ, highSize, lowSize, viewSize);
		if (obj->mapAddr && IsBadReadPtr(obj->mapAddr, 1))
		{
			CloseSendFile(obj);
			return	FALSE;
		}
	}
	return	obj->mapAddr ? TRUE : FALSE;
}

BOOL TMainWin::CloseSendFile(SendFileObj *obj)
{
	if (obj == NULL)
		return	FALSE;
	::UnmapViewOfFile(obj->mapAddr);obj->mapAddr= NULL;
	::CloseHandle(obj->hMap);		obj->hMap	= NULL;
	::CloseHandle(obj->hFile);		obj->hFile	= INVALID_HANDLE_VALUE;
	obj->offset = 0;
	return	TRUE;
}

 

发送文件线程:

DWORD WINAPI TMainWin::SendFileThread(void *_sendFileObj)
{
	SendFileObj	*obj = (SendFileObj *)_sendFileObj;
	fd_set		fds;
	fd_set		*rfds = NULL, *wfds = &fds;
	timeval		tv;
	int			sock_ret;
	BOOL		ret = FALSE, completeWait = FALSE;
	BOOL		(TMainWin::*SendFileFunc)(SendFileObj *obj) =
				obj->command == IPMSG_GETDIRFILES ? &TMainWin::SendDirFile : &TMainWin::SendFile;

	FD_ZERO(&fds);
	FD_SET(obj->conInfo->sd, &fds);

	for (int waitCnt=0; waitCnt < 180 && obj->hThread != NULL; waitCnt++)
	{
		tv.tv_sec = 1, tv.tv_usec = 0;

		if ((sock_ret = ::select(obj->conInfo->sd + 1, rfds, wfds, NULL, &tv)) > 0)
		{
			waitCnt = 0;

			if (completeWait)
			{	// dummy read 
				if (::recv(obj->conInfo->sd, (char *)&ret, sizeof(ret), 0) >= 0)
					ret = TRUE;
				break;
			}
			else if ((mainWin->*SendFileFunc)(obj) != TRUE)
				break;
			else if (obj->status == FS_COMPLETE)
			{
				completeWait = TRUE, rfds = &fds, wfds = NULL;
				if (obj->fileSize == 0) { ret = TRUE; break; }
			}
		}
		else if (sock_ret == 0) {
			FD_ZERO(&fds);
			FD_SET(obj->conInfo->sd, &fds);
		}
		else if (sock_ret == SOCKET_ERROR) {
			break;
		}
	}

	if (obj->isDir)
	{
		mainWin->CloseSendFile(obj);
		while (--obj->dirCnt >= 0)
			::FindClose(obj->hDir[obj->dirCnt]);
	}

	obj->status = ret ? FS_COMPLETE : FS_ERROR;
	mainWin->PostMessage(WM_TCPEVENT, obj->conInfo->sd, FD_CLOSE);
	::ExitThread(0);
	return	0;
}

BOOL TMainWin::SendFile(SendFileObj *obj)
{
	if (obj == NULL || obj->hFile == INVALID_HANDLE_VALUE)
		return	FALSE;

	int		size = 0;
	_int64	remain = obj->fileSize - obj->offset;
	int		transMax = cfg->TransMax - (int)(obj->offset % cfg->TransMax);

	if (remain > 0 && (size = ::send(obj->conInfo->sd, obj->mapAddr + (obj->offset % cfg->ViewMax), (int)(remain > transMax ? transMax : remain), 0)) < 0)
		return	FALSE;

	obj->offset += size;

	if (obj->offset == obj->fileSize)
		obj->status = obj->command == IPMSG_GETDIRFILES ? FS_ENDFILE : FS_COMPLETE;
	else if ((obj->offset % cfg->ViewMax) == 0)	//
{
		::UnmapViewOfFile(obj->mapAddr);
		remain = obj->fileSize - obj->offset;
		obj->mapAddr = (char *)::MapViewOfFile(obj->hMap, FILE_MAP_READ, (int)(obj->offset >> 32), (int)obj->offset, (int)(remain > cfg->ViewMax ? cfg->ViewMax : remain));
	}

	obj->conInfo->lastTick = ::GetTickCount();

	return	TRUE;
}

BOOL TMainWin::EndSendFile(SendFileObj *obj)
{
	if (obj == NULL)
		return	FALSE;

	if (obj->hThread)
	{
		HANDLE	hThread = obj->hThread;
		obj->hThread = 0;	
::WaitForSingleObject(hThread, INFINITE);
		::CloseHandle(hThread);
	}
	if (::closesocket(obj->conInfo->sd) != 0)
		obj->status = FS_ERROR;	

	CloseSendFile(obj);

	if (obj->isDir)
		free(obj->hDir);

	shareMng->EndHostShare(obj->packetNo, &obj->host->hostSub, obj->fileInfo, obj->status == FS_COMPLETE ? TRUE : FALSE);
	sendFileList.DelObj(obj);
	delete obj->conInfo;
	delete obj;
	return	TRUE;
}

 

 类似资料: