文号
828094
阅读
7339
回复
2
收藏
2
使用mailslot实现本地C/S双工通信
2016/12/04
IP:四川

Windows中有两种基于虚拟文件系统的进程间通信方式——邮件槽和管道。邮件槽的优点是简单稳定,不会动不动就管道损坏,缺点是一次传输数据不能超过64kB,而且是单工模式,只能客户端发给服务器。不过我们可以手工实现有连接的双工通信。

客户端一般采用无限长等待方式,而服务端一般采用无等待循环检查的方式实现。

项目属性里的字符集设置为[使用多字节字符集]。

客户端:

#include <stdio.h>
#include <windows.h>

char appname[] = "myapp1";

int main()
{
	char namebuf[1024] = "";
	wsprintf(namebuf, "//./mailslot/%s/listen", appname);
	HANDLE hlisten;
	while (INVALID_HANDLE_VALUE == (hlisten = CreateFile(namebuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)))
	{
	}

	DWORD threadid = GetCurrentThreadId();
	DWORD writenum = 0;
	DWORD readnum = 0;
	int clientid = -1;
	DWORD sentnum = 0;
	
	wsprintf(namebuf, "//./mailslot/%s/%u/client", appname, threadid);
	HANDLE hclient = CreateMailslot(namebuf, 0, MAILSLOT_WAIT_FOREVER, NULL);
	WriteFile(hlisten, &threadid, sizeof threadid, &writenum, NULL);
	ReadFile(hclient, &clientid, sizeof clientid, &readnum, NULL);
	CloseHandle(hlisten);
	printf("connect established, connection id %d\n", clientid);

	wsprintf(namebuf, "//./mailslot/%s/%u/server", appname, threadid);
	HANDLE hserver = CreateFile(namebuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	
	WriteFile(hserver, "来自邮件槽客户端的问候", 22, &writenum, NULL);
	ReadFile(hclient, &sentnum, sizeof sentnum, &readnum, NULL);
	printf("sent %u bytes\n", sentnum);
	
	WriteFile(hserver, "\033disconnect", 11, &writenum, NULL);
	ReadFile(hclient, &sentnum, sizeof sentnum, &readnum, NULL);
	printf("sent %u bytes\n", sentnum);
}
</windows.h></stdio.h>

服务端:

#include <stdio.h>
#include <windows.h>

char appname[] = "myapp1";
HANDLE listen_handle;
#define CONNECTION_NUM 16
HANDLE client_handle[CONNECTION_NUM];
HANDLE server_handle[CONNECTION_NUM];

int main()
{
	char namebuf[1024] = "";

	wsprintf(namebuf, "//./mailslot/%s/listen", appname);
	listen_handle = CreateMailslot(namebuf, 0, 0, NULL);
	if (listen_handle == INVALID_HANDLE_VALUE)
	{
		printf("ERR - cannot listen on %s\n", namebuf);
		return 1;
	}
	printf("SUC - listen on %s\n", namebuf);

	for (int i = 0; i < CONNECTION_NUM; i++)
	{
		client_handle[i] = INVALID_HANDLE_VALUE;
		server_handle[i] = INVALID_HANDLE_VALUE;
	}

	DWORD threadid = 0;
	DWORD readnum = 0;
	DWORD writenum = 0;
	while (true)
	{
		for (int i = 0; i < CONNECTION_NUM; i++)
		{
			if (client_handle[i] == INVALID_HANDLE_VALUE || server_handle == INVALID_HANDLE_VALUE)
			{
				CloseHandle(client_handle[i]);
				CloseHandle(server_handle[i]);
				client_handle[i] = INVALID_HANDLE_VALUE;
				server_handle[i] = INVALID_HANDLE_VALUE;

				ReadFile(listen_handle, &threadid, sizeof threadid, &readnum, NULL);
				if (readnum < sizeof threadid)
					continue;

				wsprintf(namebuf, "//./mailslot/%s/%u/client", appname, threadid);
				client_handle[i] = CreateFile(namebuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
				if (client_handle[i] == INVALID_HANDLE_VALUE)
				{
					printf("ERR - connection %d cannot open %s\n", i, namebuf);
					continue;
				}
				SetMailslotInfo(client_handle[i], 0);
				printf("SUC - connection %d open %s\n", i, namebuf);

				wsprintf(namebuf, "//./mailslot/%s/%d/server", appname, threadid);
				server_handle[i] = CreateMailslot(namebuf, 0, 0, NULL);
				if (server_handle[i] == INVALID_HANDLE_VALUE)
				{
					printf("ERR - connection %d cannot create %s\n", i, namebuf);
					CloseHandle(client_handle[i]);
					client_handle[i] = INVALID_HANDLE_VALUE;
					continue;
				}
				printf("SUC - connection %d create %s\n", i, namebuf);

				WriteFile(client_handle[i], &i, sizeof i, &writenum, NULL);
			}
			else
			{
				char buf[65537] = "";
				char msgbuf[1024] = "";
				
				ReadFile(server_handle[i], buf, 65536, &readnum, NULL);
				if (readnum == 0)
					continue;

				printf("SUC - connection %d read %u bytes: %s\n", i, readnum, buf);

				WriteFile(client_handle[i], &readnum, sizeof readnum, &writenum, NULL);

				if (lstrcmpi(buf, "\033disconnect") == 0)
				{
					CloseHandle(client_handle[i]);
					CloseHandle(server_handle[i]);
					client_handle[i] = INVALID_HANDLE_VALUE;
					server_handle[i] = INVALID_HANDLE_VALUE;

					printf("SUC - connection %d closed\n", i);
					continue;
				}
			}
		}
	}
}
</windows.h></stdio.h>
[ 修改于10年0个月前 - 2016/12/04 17:41:56 ]
10年0个月前IP:广东
828176
1楼
mailslot好像是通过UDP来传输数据的,不太可靠
可以用Socket,还是跨平台的
引用
评论
acmilan作者
10年0个月前修改于 10年0个月前IP:四川
828185
2楼
mailslot和namedpipe有一点不好,它们是网络接口,容易被dos。
如果能使用USER32的话,可以各建立一个窗口,然后用WM_COPYDATA。
如果不能使用USER32,可以用命名事件和共享内存,不过要麻烦一些。
引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的