使用mailslot实现本地C/S双工通信
acmilan2016/12/04软件综合 IP:四川

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

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

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

客户端:

<code class="language-cpp">#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></code>

服务端:

<code class="language-cpp">#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></code>

[修改于 7年5个月前 - 2016/12/04 17:41:56]

来自:计算机科学 / 软件综合
2
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
amo
7年5个月前 IP:广东
828176
mailslot好像是通过UDP来传输数据的,不太可靠
可以用Socket,还是跨平台的
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年5个月前 修改于 7年5个月前 IP:四川
828185
mailslot和namedpipe有一点不好,它们是网络接口,容易被dos。
如果能使用USER32的话,可以各建立一个窗口,然后用WM_COPYDATA。
如果不能使用USER32,可以用命名事件和共享内存,不过要麻烦一些。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}