天使の泪http://blog.yesky.com/Blog/xioxu/复制地址

私達を現実に直面させて、私達に理想に忠実にさせる

控制面板
日历
<2008年10月>
SuMoTuWeThFrSa
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
留言簿(14)
文章分类
文章档案
日记档案
c++
日语学习
收藏
.net
这篇文章将会介绍现有的Win32函数支持的网络通信功能,并且展示了如何在你自己的应用中使用它们。在API中,有两个支持网络通信的便利方法:mailslot和命名管道(name pipes)。这篇文章将分别讨论它们,并且分别介绍它们的优缺点。

  因为Win32 API直接支持网络通信,因此要创建各种使用网络的应用是特别简单的。例如,你要在你的网络中建立一个多用户会议系统,与BBS的“CB”类似。在这个系统中,用户在各自的机器上运行一个会议系统程序,他们所打的全部信息都会广播给同一网络的所有其他用户。通常这种系统可使用mailslots实现,因为mailslots很容易广播信息。事实上,多人的网络游戏使用的也是类似的技术。

  当你要在两部机器间传送大量的数据流时,你通常使用点对点的命名管道连接。例如,你会使用命名管道来实现一个网络数字电话或者视频系统。客户/服务器也都是使用命名管道的。其中的一个中央机器会作为服务器端,然后其它所有的客户端就可使用命名管道与它分别进行连接。

  网络基础

  为了进行更好地理解下面的例子,懂得一些网络的基本知识是很有必要的。下面的图展示了一个小型公司中常见的简单网络。每台机器都使用一个网络适配器连接到网络中,并且都拥有一个唯一的名字来标识它。网络适配器决定了网络的类型,常见的有以太网或者令牌网。适配器还决定了网络使用的媒体,可以是同轴线、双绞线等。要知道的一点是,在这样的简单网络中,所有的机器都可以平等地与其它的机器进行通信。



*******************图一*************************

  通过传统的Win32 API函数在机器间通信有两种方法。一种是mailslot,一个机器可以广播一个信息,网络中的其它所有机器都可以接收到。使用命名管道的话,一部机器选择另一部进行通信,并且与它建立一个特别的连接。命名管道的好处是连接可靠。如果连接打断的话,例如一块网卡或者网线出现故障,连接的两端都可以马上接收到连接断开的信息。mailslots是不可靠的,因为发送者无法确认接收者是否已经收到了信息。mailslot的好处是它可以很容易地同时给许多机器发信息。

  上面的图展示的是一个网段。一个网段的定义是直接互相连接的一组机器。一个网段中的机器数目是受到限制的,因为当机器的数量增加时,网络的通信量也会增大。通常的限制是大约为100台机器。在一个大的公司中,每个网段大约包括有20到30台机器。所有的网段之间可以通过路由器进行连接,这样它们之间就可以进行通信,如下图所示。了解到这种差别是重要的,因为通常一个mailslot信息只能在一个网段中传送,而命名管道的信息可以经过路由器传送到另一个网段中。



******************图二***********************

  使用mailslots和命名管道来进行网络通信,有三种不同的通信方式:广播、点对点和客户/服务器方式。将mailslot应用在广播模式时,一台机器发送信息到网段上的其它所有机器上。在点对点通信时,一台机器与另一台建立一个特别的连接,数据可以通过命名管道在它们之间往复传送。在一个客户/服务器的关系中,一台机器作为服务器,所有机器通过点对点的命名管道来与它进行连接。如果要使用客户/服务器的方式来模拟一个广播的操作,可以通过一台机器发送一个信息到服务器,然后服务器就可以将信息的副本分别发到每个客户端。
mailslot连接

  使用Win32 API来进行通信时,Mailslots是最简单的方式。在同一网段中,Mailslots提供了一条单向的通信路径,将信息由一个发送者传送到一个或者多个接收者中。如果你想同时发送数据到许多接收者时,可以考虑使用mailslot的方式。

  要创建mailslots是非常简单的,读取和写入都是通过API常见的ReadFile和WriteFile函数进行的。在创建mailslot时,一个特别的路径名会传送到CreateMailslot函数中,可让系统知道要创建一个mailslot而不是一个通常的文件。(这里提到的函数,你都可以从Win32编程者的参考指南、SDK或者Visual C++ V2的Win32帮助文件中找到)

  文章最后有一些程序的列表,在列表1和2中的程序已经尽量地简化,从中你可以很容易地了解到通过一个mailslot来传送和接收数据的必要步骤。列表1展示了如何通过CreateMailslto函数来创建一个mailslot服务器,服务器保存有接收信息的一个队列,直到你使用ReadFile函数来读取它们。存储在队列中的信息以它们到达的顺序排列。

  mailslot的名字必须以\\.\mailslot\[path]name的形式排列。这看来象一个文件名,事实上,它的ReadFile函数操作也与一个文件类似。有点不同的是,该函数并不会创建任何真正的文件:mailslot保存在内存中。在列表1中使用的mailslot名字是:"\\.\mailslot\sms"。为了进一步对mailslot进行分类,你可以在路径中加入“子目录”。

  在你创建mailslot时,你可以指定信息的最大长度,以及读取的超时时间。在网络上,mailslots在一个信息(message)中可传送不超过400字节的信息。如果你将超时的值设置为0,那么不管缓冲中是否有信息,对ReadFile的任何调用都会马上返回。如果你将超时时间设置为一个特定值(以毫秒计),若在定义时间内都没有任何信息到达,读取的操作将会失败。你还可以使用MAILSLOT_WAIT_FOREVER常数来创建一个阻塞的读取。

  列表1使用的是无阻塞的方式,并且使用GetMailslotInfo函数来确保在进行一个读取操作前,mailslot队列中有信息存在。该函数返回队列中信息的最大长度、下一信息的长度和等待的信息数目。程序不断地检测mailslot中是否有信息存在,如果有的话,它就读取第一个,从mailslot中读取信息的操作与读取一个文件类似。

  在网络上的任何计算机,如果要发送信息给一台运行列表1程序的机器,需要提供发送者和接收者的mailslot名字。列表2展示了如何发送信息给一个mailslot。它首先通过常用的CreateFile函数来打开一个到mailslot的可写连接。该程序作为一个mailslot客户端使用,因为它写信息到已经在网络上运行的mailslot服务器上。CreateFile函数通过检查mailslot文件的名字,就知道不是要创建一个文件,而是要与一个mailslot通信。文件名可有4种不同的格式:

\\.\mailslot\[path]name
\\*\mailslot\[path]name
\\domain\mailslot\[path]name
\\machine\mailslot\[path]name

上面的例子中,这些名字分别指定了网络上的本地机器或者某台特定的机器。第二种形式指定了一个到本地主域所有机器的广播操作。第三种形式指定了到某个域的所有机器。对于域和域控制器的详细信息,可参考NT的书籍。

  在打开mailslot后,列表2使用GetComputerName得到本地的计算机名字,然后广播该名字给当前域的所有mailslot,每5秒广播一次。

  列表1使用一个轮询的技术来检查信息。每隔半秒,它就会调用GetMailslotInfo并且检查slot中是否有信息。通常轮询在一个多线程的环境中并不是一个好的技术,因为效率比较低。你可以不用轮询,而是通过设置CreateMailslot中的超时时间为一个适当的值,然后以缓冲为0的参数调用ReadFile来等待信息的到来。一旦ReadFlie返回,你就知道有信息存在,接着可调用GetMailslotInfo和ReadFile,如列表1所示。

  当你运行列表2的程序时,它将广播给网络上的所有机器。如果你在同一或者不同的机器上运行读取器的多个副本,它们都将看到由写入者产生的信息。你也可以在网络上运行多个写入者,读取者都将会看到所有写入者产生的信息。在列表1和2的程序中,要注意到它们都是假定该程序将会从外部终止。你可以使用CloseHandle函数来关闭一个mailslot服务器或者客户端。
命名管道(Named Pipes)

  命名管道提供了一个确认的传送技术。与网络上的广播方式不同,你通过一个命名管道与另一台机器建立一个不同的连接。如果连接中断。例如是由于一台机器关掉或者网络的某部分有故障,连接的双方都可以在尝试发送或者接收时,马上知道中断的信息。通过一个命名管道,可确保包顺序到达。命名管道的唯一问题是你不能广播包了。要广播任何信息,所有的目标机器都必须与中央的服务器建立一个连接,服务器必须分别传送信息到各个不同的机器上。

  命名管道的创建只比mailslot难一点。列表4和5的程序展示了如何在两个使用命名管道的应用之间,创建一个简单的点对点连接。首先运行列表4中的接收程序,然后在同一机器上运行列表5中的发送程序。该程序将询问你要连接的机器名字。由于你在同一部机器上运行发送和接收的程序,因此可输入“.”或者是你的机器名。你将会看到每隔5秒左右,就有一个信息由发送者传到接收者上。当你关闭发送者的时候,在接收者上就会马上出现一个信息,指示它已经检测出管道连接中断。如果只启动发送的程序,发送者将会马上出错,因为它不能建立一个连接。与mailslot不同,管道可以告诉我们另一端的工作是否正常。

  命名管道连接在网络上的使用与在同一部机器上一样简单。例如,如果列表4中的服务器程序运行在一部称为“orion”的机器上,使用与该机器同样的帐号和密码在另一台不同的机器上登录,在上面运行列表5的程序,要求机器名时,输入“orion”的名字。这样连接就被正确地建立起来了。要注意一点,使用命名管道的时候,你必须要知道运行服务器的机器名字。

  你还要知道,如果使用另一个用户来尝试连接接收器时,连接将会失败。例如用户“jones”在“orion”的机器上运行接收程序,当用户“smith”尝试由另一台机器进行连接时,连接将会失败,并显示一个“拒绝访问”的错误。这是NT的安全系统造成的。你可以看NT书籍的相关部分,了解详细的原因和解决办法。

  在列表4的程序中,通过使用CreateNamedPipe来创建一个命名管道服务器。与CreateNamedPipe函数一起使用的名字通常有以下的形式:

   \\.\pipe\[path]name

与mailslot一样,你可以在管道名字的前面指定一个路径,以区分系统中不同的管道。

  传送给CreateNamedPipe的openMode参数用来决定管道的方向。命名管道可以是单向的,也可以是双向的,在于openMode参数使用的常数,包括有:

PIPE_ACCESS_DUPLEX
PIPE_ACCESS_INBOUND
PIPE_ACCESS_OUTBOUND

  CreateNamedPipe的pipeMode参数决定管道的工作方式,可以是字节流或者是称为信息的字节包。字节流是没有逻辑边界的。信息将一组的字节组合起来作为一个单元传送。你可以在读取和写入时指定一种方式:

PIPE_TYPE_MESSAGE
PIPE_TYPE_BYTE
PIPE_READMODE_MESSAGE
PIPE_READMODE_BYTE

  在一台机器上,一个管道可以有超过一个的实例。这可让一个程序处理多个的客户端,每个使用独立的线程,还必须创建一个命名管道服务器。由于列表4和5中的例子只是一个简单的点对点连接,只需要一个实例,一个实例的最大值在调用CreateNamedPipe时指定。

  列表4中的程序接着会等待一个通过ConnectNamedPipe函数建立的连接。当一个客户端程序使用正确的机器名和命名管道调用CreateFile时,就会与服务器建立起一个连接。在连接时,ConnectNamedPipe函数会返回。你可以选择指定一个交迭的结构,ConnectNamedPipe 将马上返回,然后产生连接的事件。

  列表4的程序然后进入一个循环,等待数据到达。ReadFile函数的工作与操作文件时有点不同。因为这个命名管道是信息的模式,ReadFile只要一接收到一个完整的信息,就会立刻返回。这里使用的是一个阻塞的读取,你也可以选择一个交迭的读取。

  列表5的程序是列表4的一个简单的客户。列表5首先通过CreateFile函数创建连接到命名管道。然后通过使用WriteFile函数写入信息。每次调用WriteFile都会在命名管道的接收端建立一个信息,因此接收者的ReadFile函数将在接收信息时除去阻塞,服务器就会在屏幕上显示一个信息。

  如果两个客户的副本同时尝试连接列表4中的程序,服务器将会拒绝第二个客户。不论是终止客户或者服务器,另一方都将在检测出连接中断时马上终止。

结论

  命名管道通常用在客户/服务器系统,这时服务器使用一个多线程的方式来同时处理多个连接。

  列表1

  该程序创建一个mailslot服务器并且从中进行读取


//***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// This code implements a simple mailslot server (receiver) that
// uses polling.
file://***************************************************************

// sms_recv.cpp

#include
#include

int main()
{
char toDisptxt[80];
HANDLE hSMS_Slot;
DWORD nextSize;
DWORD Msgs;
DWORD NumBytesRead;
BOOL Status;

/* Create a mailslot for receiving messages */
hSMS_Slot=CreateMailslot("\\\\.\\mailslot\\sms",
0, 0, (LPSECURITY_ATTRIBUTES) NULL);

/* Check and see if the mailslot was created */
if (hSMS_Slot == INVALID_HANDLE_VALUE)
{
cerr << "ERROR: Unable to create mailslot "
<< GetLastError() << endl;
return (1);
}

/* Repeatedly check for messages until the
program is terminated */
while(1)
{
Status=GetMailslotInfo(hSMS_Slot,
(LPDWORD) NULL, &nextSize, &Msgs,
(LPDWORD) NULL);
if (!Status)
{
cerr << "ERROR: Unable to get status. "
<< GetLastError() << endl;
CloseHandle(hSMS_Slot);
return (1);
}

/* If messages are available, then get them */
if (Msgs)
{

/* Read the message and check to see if
read was successful */
if (!ReadFile(hSMS_Slot, toDisptxt, nextSize,
&NumBytesRead, (LPOVERLAPPED) NULL))
{
cerr
<< "ERROR: Unable to read from mailslot "
<< GetLastError() << endl;
CloseHandle(hSMS_Slot);
return (1);
}

/* Display the Message */
cout << toDisptxt << endl;
}
else
/* Check for new messages twice a second */
Sleep(500);
} /* while */
}

列表2

  该程序每隔5秒写一次mailslot

//***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// This code implements a simple mailslot sender.
file://***************************************************************

// sms_send.c

// Usage: sms_send

#include
#include
#include

int main()
{
char toSendTxt[100], buffer[100];
DWORD bufferLen=100;
HANDLE hSMS_Slot;
BOOL Status;
DWORD NumBytesWritten;

/* Create the mailslot file handle for
sending messages */
hSMS_Slot=CreateFile("\\\\*\\mailslot\\sms",
GENERIC_WRITE, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

/* Check and see if the mailslot file was
opened, if not terminate program */
if (hSMS_Slot == INVALID_HANDLE_VALUE)
{
cerr << "ERROR: Unable to create mailslot "
<< GetLastError() << endl;
return (1);
}

/* form string to send */
GetComputerName(buffer, &bufferLen);
strcpy(toSendTxt, "Test string from ");
strcat(toSendTxt, buffer);

/* Repeatedly send message until program
is terminated */
while(1)
{
cout << "Sending..." << endl;
/* Write message to mailslot */
Status=WriteFile(hSMS_Slot,
toSendTxt, (DWORD) strlen(toSendTxt)+1,
&NumBytesWritten, (LPOVERLAPPED) NULL);

/* If error occurs when writing to mailslot,
terminate program */
if (!Status)
{
cerr << "ERROR: Unable to write to mailslot "
<< GetLastError() << endl;
CloseHandle(hSMS_Slot);
return (1);
}

/* Wait sending the message again */
Sleep(4800);
} /* while*/
}


 列表4

  这个简单的程序用来创建一个命名管道服务器。该服务器将会等待并接受一个连接,然后从中接收信息。

//***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// This code implements a simple named pipe server (receiver).
file://***************************************************************

// ssnprecv.cpp

// Usage: ssnprecv

#include
#include

int main()
{
 char toDisptxt[80];
 HANDLE ssnpPipe;
 DWORD NumBytesRead;

 /* Create a named pipe for receiving messages */
 ssnpPipe=CreateNamedPipe("\\\\.\\pipe\\ssnp",PIPE_ACCESS_INBOUND,
PIPE_TYPE_MESSAGE | PIPE_WAIT,1, 0, 0, 150,(LPSECURITY_ATTRIBUTES) NULL);

 /* Check and see if the named pipe was created */
 if (ssnpPipe == INVALID_HANDLE_VALUE)
 {
  cerr << "ERROR: Unable to create a named pipe. " << endl;
  return (1);
 }

 /* Allow a client to connect to the name pipe,
 terminate if unsuccessful */
 cout << "Waiting for connection... " << endl;
 if(!ConnectNamedPipe(ssnpPipe, (LPOVERLAPPED) NULL))
 {
  cerr << "ERROR: Unable to connect a named pipe "<< GetLastError() << endl;
  CloseHandle(ssnpPipe);
  return (1);
 }

 /* Repeatedly check for messages until the program
is terminated */
 while(1)
 {
  /* Read the message and check to see if read
  was successful */
  if (!ReadFile(ssnpPipe, toDisptxt,sizeof(toDisptxt),&NumBytesRead, (LPOVERLAPPED) NULL))
  {
   cerr << "ERROR: Unable to read from named pipe " << GetLastError() << endl;
   CloseHandle(ssnpPipe);
   return (1);
  }

  /* Display the Message */
  cout << toDisptxt << endl;
 } /* while */
}


列表5

  一个命名管道客户,可连接到列表4中的程序,并且发送信息给它

//***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// Copyright 1994, by Prentice Hall.
file://
// This code implements a simple named pipe sender.
file://***************************************************************

// ssnpsend.cpp

// Usage: ssnpsend

#include
#include

int main()
{
 char *toSendtxt="Test String";
 HANDLE ssnpPipe;
 DWORD NumBytesWritten;
 char machineName[80];
 char pipeName[80];

 cout << "Enter name of server machine: ";
 cin >> machineName;
 wsprintf(pipeName, "\\\\%s\\pipe\\ssnp",machineName);

 /* Create the named pipe file handle for sending messages */
 ssnpPipe=CreateFile(pipeName,GENERIC_WRITE, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

/* Check and see if the named pipe file was
opened, if not terminate program */
 if (ssnpPipe == INVALID_HANDLE_VALUE)
 {
  cerr << "ERROR: Unable to create a named pipe "<< endl;
  cerr << GetLastError() << endl;
  return (1);
 }

 /* Repeatedly send message until program is terminated */
 while(1)
 {
  cout << "Sending..." << endl;
  /* Write message to the pipe */
  if (!WriteFile(ssnpPipe,toSendtxt, (DWORD) strlen(toSendtxt)+1,
&NumBytesWritten, (LPOVERLAPPED) NULL))
  {
   /* If error occurs when writing to named
   pipe, terminate program */
   cerr << "ERROR: Unable to write to named pipe "<< GetLastError() << endl;
   CloseHandle(ssnpPipe);
   return (1);
  }

 /* Wait before sending the message again */
 Sleep(4800);
 } /* while*/
}



作者:天使之泪 阅读() 评论()  编辑 发表于:2005-05-25 09:21
相关内容
文章评论

暂无人对此文章发表评论!

发表评论
标题 *  
姓名 *  
内容 *  
   验证码: *       
       
版权声明:天极是本Blog托管服务提供商。如本文牵涉版权问题,天极不承担相关责任,请版权拥有者直接与文章作者联系解决。
Powered by:

Copyright © 天使之泪