首页
论坛
专栏
课程

Windows系统编程之进程间通信

2006-5-25 14:18 35718

Windows系统编程之进程间通信

2006-5-25 14:18
35718
Windows系统编程之进程间通信
作者:北极星2003
来源:看雪论坛(www.pediy.com)
Windows 的IPC(进程间通信)机制主要是异步管道和命名管道。(至于其他的IPC方式,例如内存映射、邮槽等这里就不介绍了)
管道(pipe)是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端。一个进程向管道写入信息,而另外一个进程从管道读取信息。
异步管道是基于字符和半双工的(即单向),一般用于程序输入输出的重定向;命名管道则强大地多,它们是面向消息和全双工的,同时还允许网络通信,用于创建客户端/服务器系统。
一、异步管道(实现比较简单,直接通过实例来讲解)
实验目标:当前有sample.cpp, sample.exe, sample.in这三个文件,sample.exe为sample.cpp的执行程序,sample.cpp只是一个简单的程序示例(简单求和),如下:
#include <iostream.h>
int main()
{
	int a, b ;
	while ( cin >> a >> b && ( a || b ) )
		cout << a + b << endl ;
	return 0;
}

Sample.in文件是输入文件,内容:
32 433
542 657
0 0
要求根据sample.exe和它的输入数据,把输出数据重定向到sample.out
流程分析:实际这个实验中包含两个部分,把输入数据重定向到sample.exe 和把输出数据重定向到sample.out。在命令行下可以很简单的实现这个功能“sample <sample.in >sample.out”,这个命令也是利用管道特性实现的,现在我们就根据异步管道的实现原理自己来实现这个功能。
管道是基于半双工(单向)的,这里有两个重定向的过程,显然需要创建两个管道,下面给出流程图:

异步管道实现的流程图说明:
1)。父进程是我们需要实现的,其中需要创建管道A,管道B,和子进程,整个实现流程分为4个操作。
2)。管道A:输入管道
3)。管道B:输出管道
4)。操作A:把输入文件sample.in的数据写入输入管道(管道A)
5)。操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,这里实现输入重定向,即把输入管道作为输入设备。
6)。操作C:子进程把加工后的成品(输出数据)输出到输出管道。通常,程序的输出数据会输出到标准的输出设备,一般为屏幕,这里实现输出重定向,即把输出管道作为输出设备。
7)。操作D:把输出管道的数据写入输出文件
需要注意的是,管道的本质只是一个共享的内存区域。这个实验中,管道区域处于父进程的地址空间中,父进程的作用是提供环境和资源,并协调子进程进行加工。
程序源码:
#include <windows.h> 
#include <iostream.h>

const int BUFSIZE = 4096 ; 
HANDLE	hChildStdinRd, hChildStdinWr, hChildStdinWrDup, 
	     hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup, 
		hSaveStdin,		hSaveStdout; 

BOOL CreateChildProcess(LPTSTR); 
VOID WriteToPipe(LPTSTR); 
VOID ReadFromPipe(LPTSTR); 
VOID ErrorExit(LPTSTR); 
VOID ErrMsg(LPTSTR, BOOL); 
void main( int argc, char *argv[] ) 
{	
	// 处理输入参数
	if ( argc != 4 )
		return ;

	// 分别用来保存命令行,输入文件名(CPP/C),输出文件名(保存编译信息)
	LPTSTR lpProgram = new char[ strlen(argv[1]) ] ;
	strcpy ( lpProgram, argv[1] ) ;
	LPTSTR lpInputFile = new char[ strlen(argv[2]) ];
	strcpy ( lpInputFile, argv[2] ) ;
	LPTSTR lpOutputFile = new char[ strlen(argv[3]) ] ;
	strcpy ( lpOutputFile, argv[3] ) ;		
	
	SECURITY_ATTRIBUTES saAttr; 
	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
	saAttr.bInheritHandle = TRUE; 
	saAttr.lpSecurityDescriptor = NULL; 
	 
	/************************************************
	 *		redirecting child process's STDOUT	*
	 ************************************************/
	hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
	
	if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) 
		ErrorExit("Stdout pipe creation failed\n"); 
		
	if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) 
		ErrorExit("Redirecting STDOUT failed"); 
	
	BOOL fSuccess = DuplicateHandle(
		GetCurrentProcess(), 
		hChildStdoutRd,
        GetCurrentProcess(), 
		&hChildStdoutRdDup ,
		0,
        FALSE,
        DUPLICATE_SAME_ACCESS);
    if( !fSuccess )
        ErrorExit("DuplicateHandle failed");
    CloseHandle(hChildStdoutRd);
	
	/************************************************
	 *		redirecting child process's STDIN		*
	 ************************************************/
	hSaveStdin = GetStdHandle(STD_INPUT_HANDLE); 

	if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) 
		ErrorExit("Stdin pipe creation failed\n"); 
	
	if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) 
		ErrorExit("Redirecting Stdin failed"); 
	
	fSuccess = DuplicateHandle(
		GetCurrentProcess(), 
		hChildStdinWr, 
		GetCurrentProcess(),
		&hChildStdinWrDup, 
		0, 
		FALSE,                 
		DUPLICATE_SAME_ACCESS); 
	if (! fSuccess) 
		ErrorExit("DuplicateHandle failed"); 
	CloseHandle(hChildStdinWr); 	

	/************************************************
	 *			创建子进程(即启动SAMPLE.EXE)		*
	 ************************************************/
	fSuccess = CreateChildProcess( lpProgram );
	if ( !fSuccess ) 
		ErrorExit("Create process failed"); 
	
	// 父进程输入输出流的还原设置
	if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) 
		ErrorExit("Re-redirecting Stdin failed\n"); 
	if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) 
		ErrorExit("Re-redirecting Stdout failed\n"); 

	WriteToPipe( lpInputFile ) ;
	ReadFromPipe( lpOutputFile ); 
          delete lpProgram ;
          delete lpInputFile ;
          delete lpOutputFile ;
} 

BOOL CreateChildProcess( LPTSTR lpProgram ) 
{ 
	PROCESS_INFORMATION piProcInfo; 
	STARTUPINFO siStartInfo;
	BOOL bFuncRetn = FALSE; 
	
	ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
	ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
	siStartInfo.cb = sizeof(STARTUPINFO); 
	
	bFuncRetn = CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, \
								0, NULL, NULL, &siStartInfo, &piProcInfo);
	if (bFuncRetn == 0) 
	{
		ErrorExit("CreateProcess failed\n");
		return 0;
	} 
	else 
	{
		CloseHandle(piProcInfo.hProcess);
		CloseHandle(piProcInfo.hThread);
		return bFuncRetn;
	}
}

VOID WriteToPipe( LPTSTR lpInputFile ) 
{ 
	HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL, 
		OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 
	if (hInputFile == INVALID_HANDLE_VALUE) 
		return ;

	BOOL fSuccess ;
	DWORD dwRead, dwWritten; 
	CHAR chBuf[BUFSIZE] = {0} ; 
	
	for (;;) 
	{ 
		fSuccess = ReadFile( hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ;
		if ( !fSuccess || dwRead == 0)
			break; 

		fSuccess = WriteFile( hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL) ;
		if ( !fSuccess ) 
			break; 
	} 
		
	if (! CloseHandle(hChildStdinWrDup)) 
		ErrorExit("Close pipe failed\n"); 

	CloseHandle ( hInputFile ) ;
} 

VOID ReadFromPipe( LPTSTR lpOutputFile ) 
{ 
	HANDLE hOutputFile = CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE, 
		FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
	if (hOutputFile == INVALID_HANDLE_VALUE) 
		return ;

	BOOL fSuccess ;
	DWORD dwRead, dwWritten; 
	CHAR chBuf[BUFSIZE] = { 0 }; 
	
	if (!CloseHandle(hChildStdoutWr)) 
		ErrorExit("Closing handle failed"); 
	
	for (;;) 
	{ 
		fSuccess = ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) ;
		if( !fSuccess || dwRead == 0) 
		{
			break; 
		}
		fSuccess = WriteFile( hOutputFile, chBuf, dwRead, &dwWritten, NULL) ;
		if ( !fSuccess ) 
			break; 
	} 

	CloseHandle ( hOutputFile ) ;
} 
VOID ErrorExit (LPTSTR lpszMessage) 
{ 
	MessageBox( 0, lpszMessage, 0, 0 ); 
}

二、命名管道
命名管道具有以下几个特征:
(1)命名管道是双向的,所以两个进程可以通过同一管道进行交互。
(2)命名管道不但可以面向字节流,还可以面向消息,所以读取进程可以读取写进程发送的不同长度的消息。
(3)多个独立的管道实例可以用一个名称来命名。例如几个客户端可以使用名称相同的管道与同一个服务器进行并发通信。
(4)命名管道可以用于网络间两个进程的通信,而其实现的过程与本地进程通信完全一致。
实验目标:在客户端输入数据a和b,然后发送到服务器并计算a+b,然后把计算结果发送到客户端。可以多个客户端与同一个服务器并行通信。
界面设计:

难点所在:
实现的过程比较简单,但有一个难点。原本当服务端使用ConnectNamedPipe函数后,如果有客户端连接,就可以直接进行交互。原来我在实现过程中,当管道空闲时,管道的线程函数会无限(INFINITE)阻塞。若现在需要停止服务,就必须结束所有的线程,TernimateThread可以作为一个结束线程的方法,但我基本不用这个函数。一旦使用这个函数之后,目标线程就会立即结束,但如果此时的目标线程正在操作互斥资源、内核调用、或者是操作共享DLL的全局变量,可能会出现互斥资源无法释放、内核异常等现象。这里我用重叠I/0来解决这个问题,在创建PIPE时使用FILE_FLAG_OVERLAPPED标志,这样使用ConnectNamedPipe后会立即返回,但线程的阻塞由等待函数WaitForSingleObject来实现,等待OVERLAPPED结构的事件对象被设置。
客户端主要代码:
void CMyDlg::OnSubmit() 
{
	// 打开管道
	HANDLE hPipe = CreateFile("\\\\.\\Pipe\\NamedPipe", GENERIC_READ | GENERIC_WRITE, \
		0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ;
	if ( hPipe == INVALID_HANDLE_VALUE )
	{
		this->MessageBox ( "打开管道失败,服务器尚未启动,或者客户端数量过多" ) ;
		return ;
	}

	DWORD nReadByte, nWriteByte ;
	char szBuf[1024] = {0} ;
	// 把两个整数(a,b)格式化为字符串
	sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;
	// 把数据写入管道
	WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;

	memset ( szBuf, 0, sizeof(szBuf) ) ;
	// 读取服务器的反馈信息
	ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;
	// 把返回信息格式化为整数
	sscanf ( szBuf, "%d", &(this->nResValue) ) ;
	this->UpdateData ( false ) ;
	CloseHandle ( hPipe ) ;
}

服务端主要代码:
// 启动服务
void CMyDlg::OnStart() 
{
	CString lpPipeName = "\\\\.\\Pipe\\NamedPipe" ;
	for ( UINT i = 0; i < nMaxConn; i++ )
	{
		// 创建管道实例
		PipeInst[i].hPipe =	CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, \
					PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ;
		if ( PipeInst[i].hPipe == INVALID_HANDLE_VALUE )
		{
			DWORD dwErrorCode = GetLastError () ;
			this->MessageBox ( "创建管道错误!" ) ;
			return ;
		}
		// 为每个管道实例创建一个事件对象,用于实现重叠IO
		PipeInst[i].hEvent  =  CreateEvent ( NULL, false, false, false ) ;
		// 为每个管道实例分配一个线程,用于响应客户端的请求
		PipeInst[i].hTread = AfxBeginThread ( ServerThread, &PipeInst[i], THREAD_PRIORITY_NORMAL ) ;
	}
	
	this->SetWindowText ( "命名管道实例之服务器(运行)" ) ;
	this->MessageBox ( "服务启动成功" ) ;
}
// 停止服务
void CMyDlg::OnStop() 
{
	DWORD dwNewMode = PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ;
	for ( UINT i = 0; i < nMaxConn; i++ )
	{
		SetEvent ( PipeInst[i].hEvent ) ;
		CloseHandle ( PipeInst[i].hTread ) ;
		CloseHandle ( PipeInst[i].hPipe ) ;
	}
		
	this->SetWindowText ( "命名管道实例之服务器" ) ;
	this->MessageBox ( "停止启动成功" ) ;
}

// 线程服务函数
UINT ServerThread ( LPVOID lpParameter )
{
	DWORD	nReadByte = 0, nWriteByte = 0, dwByte = 0 ;	
	char	szBuf[MAX_BUFFER_SIZE] = {0} ;
	PIPE_INSTRUCT	CurPipeInst = *(PIPE_INSTRUCT*)lpParameter ;
	OVERLAPPED OverLapStruct = { 0, 0, 0, 0, CurPipeInst.hEvent } ;
	while ( true )
	{
		memset ( szBuf, 0, sizeof(szBuf) ) ;	
		// 命名管道的连接函数,等待客户端的连接(只针对NT)
		ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;
		// 实现重叠I/0,等待OVERLAPPED结构的事件对象
		WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;
		// 检测I/0是否已经完成,如果未完成,意味着该事件对象是人工设置,即服务需要停止
		if ( !GetOverlappedResult ( CurPipeInst.hPipe, &OverLapStruct, &dwByte, true ) )
			break ;

		// 从管道中读取客户端的请求信息
		if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )
		{
			MessageBox ( 0, "读取管道错误!", 0, 0 ) ;
			break ;
		}
		
		int a, b ;
		sscanf ( szBuf, "%d %d", &a, &b ) ;
		pMyDlg->nFirst		= a ;
		pMyDlg->nSecond		= b ;
		pMyDlg->nResValue	= a + b ;
		memset ( szBuf, 0, sizeof(szBuf) ) ;
		sprintf ( szBuf, "%d", pMyDlg->nResValue ) ;
		// 把反馈信息写入管道
		WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
		pMyDlg->SetDlgItemInt ( IDC_FIRST, a, true ) ;
		pMyDlg->SetDlgItemInt ( IDC_SECOND, b, true ) ;
		pMyDlg->SetDlgItemInt ( IDC_RESULT, pMyDlg->nResValue, true ) ;
		// 断开客户端的连接,以便等待下一客户的到来
		DisconnectNamedPipe ( CurPipeInst.hPipe ) ;
	}

	return 0 ;
}


[公告][征集寄语] 看雪20周年年会 | 感恩有你,一路同行

上传的附件:
最新回复 (44)
AsmCCPlus 2006-5-25 14:25
2
0
果然是好东东.....谢谢楼主......学习了.........
ljkbbc 2006-5-25 14:55
3
0
study...有没有VB版的
prince 16 2006-5-25 15:11
4
0
勤奋啊~
heXer 3 2006-5-25 15:21
5
0
三天不学习,敢不上LSQ
三天不用功,敢不上MZD
thinkSJ 4 2006-5-25 15:26
6
0
HOHO....学完之后提点建议

  LPTSTR lpProgram = new char( sizeof(argv[1]) ) ;
  strcpy ( lpProgram, argv[1] ) ;
  LPTSTR lpInputFile = new char( sizeof(argv[2]) ) ;
  strcpy ( lpInputFile, argv[2] ) ;
  LPTSTR lpOutputFile = new char( sizeof(argv[3]) ) ;
  strcpy ( lpOutputFile, argv[3] ) ;   

这段内存没有被释放,另外这个写法好像也有点问题(虽然可以正常运行),不知这样改一下如何:
  LPTSTR lpProgram = new char[ sizeof(argv[1]) ] ;
  strcpy ( lpProgram, argv[1] ) ;
  LPTSTR lpInputFile = new char[ sizeof(argv[2]) ] ;
  strcpy ( lpInputFile, argv[2] ) ;
  LPTSTR lpOutputFile = new char[ sizeof(argv[3]) ] ;
  strcpy ( lpOutputFile, argv[3] ) ;   

吹毛求疵之举,兄弟不要见怪
lnn1123 13 2006-5-25 16:23
7
0
学习!
dwing 1 2006-5-25 17:58
8
0
最初由 thinkSJ 发布
LPTSTR lpProgram = new char( sizeof(argv[1]) ) ;
strcpy ( lpProgram, argv[1] ) ;
LPTSTR lpInputFile = new char( sizeof(argv[2]) ) ;
strcpy ( lpInputFile, argv[2] ) ;
LPTSTR lpOutputFile = new char( sizeof(argv[3]) ) ;
strcpy ( lpOutputFile, argv[3] ) ;
这段内存没有被释放,另外这个写法好像也有点问题(虽然可以正常运行),不知这样改一下如何:
LPTSTR lpProgram = new char[ sizeof(argv[1]) ] ;
strcpy ( lpProgram, argv[1] ) ;
LPTSTR lpInputFile = new char[ sizeof(argv[2]) ] ;
strcpy ( lpInputFile, argv[2] ) ;
LPTSTR lpOutputFile = new char[ sizeof(argv[3]) ] ;
strcpy ( lpOutputFile, argv[3] ) ;

我认为应该改成:
  LPTSTR lpProgram = new char[ strlen(argv[1])+1 ] ;
  strcpy ( lpProgram, argv[1] ) ;
  LPTSTR lpInputFile = new char[ strlen(argv[2])+1 ] ;
  strcpy ( lpInputFile, argv[2] ) ;
  LPTSTR lpOutputFile = new char[ strlen(argv[3])+1 ] ;
  strcpy ( lpOutputFile, argv[3] ) ;
因为sizeof(argv[1])=4.
alpsdew 4 2006-5-25 18:32
9
0
学习,学习 !
laomms 34 2006-5-25 19:38
10
0
值得学习!
thinkSJ 4 2006-5-25 19:54
11
0
学习!!
Lenus 3 2006-5-25 20:24
12
0
正需要这方面的东西呢
北极星2003 25 2006-5-25 20:59
13
0
6楼的正确!另外8楼的也对

多谢给意见!
写的仓促,我会尽快修改下
shiny 2006-5-27 01:42
14
0

好东西,收下了
萝卜 1 2006-6-2 09:29
15
0
难得的好资料
无影 2006-6-3 10:08
16
0
只是来学习ing!
wenben 2006-6-3 22:06
17
0
学习ing
fengercn 2006-6-4 15:28
18
0
谢谢楼主的好东东
cgdxxx 2006-6-13 16:24
19
0
强人,学习到了新技术
figofuture 2006-6-13 21:05
20
0
5)。操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,一般为屏幕,这里实现输入重定向,即把输入管道作为输入设备。

楼主说的STDIN一般应该是键盘鼠标之类的吧!屏幕应该只是STDOUT,不知道楼主是怎么理解的?
北极星2003 25 2006-6-13 21:37
21
0
Originally posted by figofuture
5)。操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,一般为屏幕,这里实现输入重定向,即把输入管道作为输入设备。

楼主说的STDIN一般应该是键盘鼠标之类的吧!屏幕应该只是STDOUT,不知道楼主是怎么理解的?


多写指出
lykjzmcc 2006-6-15 17:08
22
0
写得真是不错!管道流程图描述听清晰的!学习学习!
独孤无剑 2006-6-21 05:44
23
0
学习之中...............
ateddybear 2006-6-25 22:39
24
0
辛苦楼主了.
ateddybear 2006-6-26 16:34
25
0
首先对楼主贡献的资料深感谢意.刚好要用到这方面的东西,真是雪中送碳.
但这里还有一些问题要请教楼主
1)在查找命名管道资料地过程中也看了<WINDOWS网络编程技术>这本书,其中讲到
  实现对管道实例的处理有两种方法,一是通过多线程,每个线程处理一个实例,
   二是通过IO重叠机制.但楼主的例子中两者都用到了.所以我想了解一下IO重叠机制的原理是什么?在什么情况下用得着?

2)还麻烦楼主帮我解决一下我遇到的问题,
我的应用程序中有两个服务进程,要达到的目的是这样:

1)第一个服务器进程通过命名管道向n个客户端进程传送数据(单向的),服务器写客户机收。

2)第二个服务进程跟第一个刚好相反,n个客户端进程将处理好的数据传给第二个服务端进程,客户端写服务端收。

因为服务器端进程还涉及到与外界交互,用多线程的话又涉及数据共享数据问

题,要考虑并发,因此想用一个线程处理多个管道实例.另外,我这个应用中,

服务端进程发送的数据是随机的,各个客户端进程处理的效率也不一样(但可以

保证不会因为客户端进程来不及处理和接收数据造成数据丢失),因此还需要注

意哪些问题来避免传输过程中数据丢失?

在这里先向楼主和各位前辈谢过了!

QQ:68844959
MSN:HE_YIBIN@SINA.COM
北极星2003 25 2006-6-26 17:32
26
0
最初由 ateddybear 发布
首先对楼主贡献的资料深感谢意.刚好要用到这方面的东西,真是雪中送碳.
但这里还有一些问题要请教楼主
1)在查找命名管道资料地过程中也看了<WINDOWS网络编程技术>这本书,其中讲到
实现对管道实例的处理有两种方法,一是通过多线程,每个线程处理一个实例,
二是通过IO重叠机制.但楼主的例子中两者都用到了.所以我想了解一下IO重叠机制的原理是什么?在什么情况下用得着?
........


IO重叠的目标是实现异步I/0
个人感觉在C/S中使用管道是个比较好的选择
每个客户端都使用一个特定的管道与服务器进行独立交互
与TCP有点相似的是,在进行数据传输之前,会建立连接
另外,客户端有处理的速度不通的问题,可以使用异步I/0,配合等待函数
一般使用的是一个管道对应一个线程,
如果客户端多的话,可以使用线程池,即多量的管道对应少量的线程
关于这方面知识,过两天(大概28号以后)我会详细介绍的,最近脱不开身
bates 2006-6-26 17:34
27
0
to ateddybear:
    我简单的看了下代码,北极星并没有实现重叠IO,虽然文章里面提到了重叠IO,但是他并没有实现。
    他还是用每个客户端一个线程的方法来处理的。
    要实现重叠IO的话,代码绝对不止这些,还要复杂N倍,你到网上搜一下IOCP,就会有N多例子代码。
    ATL里面也有个简单的IOCP实现,可以看一下。
   
北极星:你在没有搞清楚一些问题之前,最好不要发表出来,会误导别人的。(或者我对重叠IO的理解,跟你的理解不一样)
nbw 24 2006-6-26 17:35
28
0
最初由 北极星2003 发布
IO重叠的目标是实现异步I/0
个人感觉在C/S中使用管道是个比较好的选择
每个客户端都使用一个特定的管道与服务器进行独立交互
与TCP有点相似的是,在进行数据传输之前,会建立连接
另外,客户端有处理的速度不通的问题,可以使用异步I/0,配合等待函数
........


同情以下
bates 2006-6-26 17:39
29
0
北极星老大要写完成端口了啊
搬个凳子学习。
北极星2003 25 2006-6-26 17:57
30
0
最初由 bates 发布
to ateddybear:
我简单的看了下代码,北极星并没有实现重叠IO,虽然文章里面提到了重叠IO,但是他并没有实现。
他还是用每个客户端一个线程的方法来处理的。
要实现重叠IO的话,代码绝对不止这些,还要复杂N倍,你到网上搜一下IOCP,就会有N多例子代码。
ATL里面也有个简单的IOCP实现,可以看一下。
........


重叠I/0需要使用OVERLAPPED结构,可以用于I0CP(I0完成端口)
完成端口用于异步方式的重叠I/0情况下,当然重叠I/O不一定非使用完成端口不可
在我的代码里并没有实现I0CP,但是重叠I/0是有的
而刚才这位朋友问我的是重叠I/0,并非I0CP
我建议先去看下重叠I/0的定义

我觉得你的话说的太过分了
首先我不会在没把问题想清楚之前给予答复
当然从哲学上来说,没有什么东西是肯定正确的
所以我的答复只能代表我的观点

你有权利对我的每一个帖子进行批驳,我也会非常欢迎你的存在
如果有一个能在我出现错误的时候提醒我,是种不错的 感觉
我为盾,你作矛,希望矛与盾共同成长

但是在这个游戏中有一个我们都必须遵守一个原则“尊重对方”
我不会和任何不懂尊重为何物的人有任何的关系
drwch 3 2006-6-26 18:03
31
0
27楼怎么总是找北极星的碴呢?你根本就是把IO CompletionPort和Overlapped IO混为一谈了,说人家误导他人,先好好看一下你自己有没有犯错吧
bates 2006-6-26 18:20
32
0
最初由 drwch 发布
27楼怎么总是找北极星的碴呢?你根本就是把IO CompletionPort和Overlapped IO混为一谈了,说人家误导他人,先好好看一下你自己有没有犯错吧


我当然知道重叠IO和完成端口是两回事,重叠io是完成端口的基础。
重叠IO能作的,完成端口也能作

我这样说只是为了提醒北极星注意而已,并没有别的意思。
bates 2006-6-26 18:28
33
0
网速真慢

北极星的建议我会采用的。
你的程序的确也不错,一个管道一个线程,这个重叠IO用的真好。
ateddybear 2006-6-27 23:04
34
0
再次谢谢版主了!
lgd 2006-7-4 21:33
35
0
终于找到了
zhuliang 5 2006-7-6 14:41
36
0
谢谢『Win32/Win64编程』版主!好东西!
zhuliang 5 2006-7-15 17:32
37
0
最初由 laomms 发布
值得学习!
sunbaiy 2006-7-18 15:01
38
0
开场白中对管道的描述能否更宏观点,如:所用的场合、威力(原谅用词),近似或竞争的技术,技术特点大比拼等。让读者知道,他就是暂时不深入了解管道,但在该用的场合一定不会忘记它,而且选择了最合适恰当的技术。

上传的文件为:VC工程化的项目,非常便于调试。工具用的好,效益太多了。

该在这里回了。
上传的附件:
sunbaiy 2006-7-18 15:24
39
0
可以告诉读者命名管道的问题:
1、仅局域网中适用,还是可用于互联网。
2、如果用于互联网,采用什么协议?如何穿透防火墙?
3、近似的信息通信(远程对象访问),如早期的CORBAR、后来的j2ee和.Net,他们的信息交互和反馈方便多了,编程时思路也清晰。因此,你要突出你的特定的适用性,让读者知道什么时候该选命名管道,什么时候选其它的。
CoolGo 2006-8-11 15:37
40
0
谢谢北极星2003给我们带来这么多的学习资料!希望学习后也能给论坛做出贡献!
adly 2006-8-21 11:26
41
0
非常好啊

我这个暑假看了那本《软件加密技术内幕》觉得非常好,所以我就去买了一本。

可让我感到有点可惜的是,我竟然晚知道了这本书的内容有一年之久。
如果能在一年前看到这本书的话,那就Perfect了。
taishan911 2006-9-7 17:10
42
0
LPTSTR lpProgram = new char( sizeof(argv[1]) ) ;
  strcpy ( lpProgram, argv[1] ) ;

上面sizeof(argv[1]) 应该是strlen(argv[1]+1)吧,如果不是为何不直接new char[4];
wangwolue 2006-9-8 16:17
43
0
谢谢楼主的贡献。不过程序中还有一个小问题我搞不定:

当客户端发送空的数据时也就是szBuf的长度为0时

// 把数据写入管道
        WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;

服务端就会挂在ServerThread这里:
// 从管道中读取客户端的请求信息
                if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )
                {
                        MessageBox ( 0, "读取管道错误!", 0, 0 ) ;
                        break ;
                }

换句话说当客户端的WriteFile中的szBuf为0时,服务端会阻塞在ReadFile
北极星2003 25 2006-9-8 16:57
44
0
最初由 taishan911 发布
LPTSTR lpProgram = new char( sizeof(argv[1]) ) ;
strcpy ( lpProgram, argv[1] ) ;

上面sizeof(argv[1]) 应该是strlen(argv[1]+1)吧,如果不是为何不直接new char[4];

你说的对
这个错误8楼也提出过了,也不知道怎么回事忘了改了
北极星2003 25 2006-9-8 16:59
45
0
最初由 wangwolue 发布
谢谢楼主的贡献。不过程序中还有一个小问题我搞不定:

当客户端发送空的数据时也就是szBuf的长度为0时

// 把数据写入管道
........


当写入0字节的消息时,ReadFile确实会等待
这也是个BUG,应该在写入消息时检测消息长度,若长度为0,则给出提示信息且不让写

多谢指出
游客
登录 | 注册 方可回帖
返回