异步非阻塞的socket通信
2011-03-02 16:34阅读:
本文介绍如何使用非阻塞方式的Socket通信,并且创建了一个聊天程序的例子来帮助说明。
介绍
本文介绍如何在多个应用程序之间创建和使用TCP/IP
Socket来进行通信。这些应用程序可以运行在同一台机器,也可以在局域网内,甚至也可以是跨越Internet的*。这种方法的好处是不需要你自己来使用线程,而是通过调用Socket的非阻塞模式来实现。在例子中:服务器创建病侦听客户端的连接,一旦有客户连接,服务器就将其加入到一个活动客户的列表中,某个客户端发送的消息也有服务器发送到各个连接的客户端,就好像聊天室中的那样。或许Remoting
(远程调用)是做这种工作更好的办法,但是我们这里还是来学习学习如何使用Socket来实现。
*注意:跨越Internet的通讯要求服务器有独立的IP地址并且不在代理或是放火墙之后。
事件时序
服务器必须要先侦听,客户端才能够连接。下面的图例说明了在一个异步Socket会话中的事件时序。
运行示例
实例代码分为两部分:ChatServer 和ChatClient. 我们首先来创建ChatServer
,然后使用下面的Telnet命令来测试它。
C代码
- telnet {server machine IP
address or machine name} 399
- telnet 10.328.32.76 399
telnet {server machine IP address or machine name} 399 telnet
10.328.32.76 399
这时,服务器上应该出现一条消息来表明这个客户连接的地址和端口。在任一个telnet窗口中键入的字符都会回显到所有与服务器连接的telnet的窗口中。试试从多台机器上并发连接服务器。不要使用localhost或者127.0.0.1来作为服务器程序唯一的侦听地址。
然后运行ChatClient实例作相同的试验和多个客户端和多个telnet并存的测试。
为什么要使用.NET的Socket?
.NET在很多地方都用到了sockets,比如:WebServices和Remoting。但是在那些应用中底层的Socket支持已经做好了,不需要直接使用。但是,和其他非.NET系统的Socket打交道或简单通信的场合中Socket的使用还是很有必要的。它可以用来和诸如DOS,Windows和UNIX系统进行通信。底层的Socket应用也可以让你减少了诸如组测,权限,域(domains),用户ID,密码等这些麻烦的安全方面的顾虑。
ChatServer /
Listener
服务器侦听端口,当有连接请求时,接受该连接并返回一条欢迎信息。在例子中客户连接被加到一个活动客户列表m_aryClients中去。这个列表会根据客户加入和离开作相应的增删。在某些情况下可能会丢失连接,所以在实际的系统中还应该有轮询侦测客户端是否在线的部分。当服务器端的listener收到客户端发来的信息后,它会把消息广播到所有连接的客户端。
下面讨论两种侦听的方法,一个是用轮询(polling),另外一个在使用事件来侦测连接的请求。
方法1 – 使用轮询的 TcpListener
System.Net.Sockets中的TcpListener
类为我们提供了一个侦听和处理客户连接的简单手段。下面的代码侦听连接,接受连接,并且向客户连接发回一个带有时间戳的欢迎信息。如果有另外一个连接请求到来,原来的连接将会丢失。注意,欢迎信息是采用ASCII编码,而不是UNICODE。
Java代码
- private Socket client
= null;
- const int
nPortListen = 399;
- try
- {
- TcpListener
listener = new
TcpListener( nPortListen );
- Console.WriteLine(
'Listening as {0}',
listener.LocalEndpoint );
- listener.Start();
- do
- {
-
byte [] m_byBuff
= new
byte[127];
-
if( listener.Pending()
)
-
{
-
client =
listener.AcceptSocket();
-
// Get
current date and time.
-
DateTime now =
DateTime.Now;
-
string strDateLine
= 'Welcome '
+ now.ToString('G')
+ '\r';
-
-
//
Convert to byte array and
send.
-
Byte[] byteDateLine
= System.Text.Encoding.ASCII.GetBytes(
strDateLine.ToCharArray() );
-
client.Send(
byteDateLine, byteDateLine.Length,
0
);
-
}
-
else
-
{
-
Thread.Sleep(
100
);
-
}
- }
while(
true );
// Don't use this.
- }
- catch( Exception ex )
- {
- Console.WriteLine (
ex.Message );
- }
private Socket client = null; const int nPortListen = 399; try {
TcpListener listener = new TcpListener( nPortListen );
Console.WriteLine( 'Listening as {0}', listener.LocalEndpoint );
listener.Start(); do { byte [] m_byBuff = new byte[127]; if(
listener.Pending() ) { client = listener.AcceptSocket(); // Get
current date and time. DateTime now = DateTime.Now; string
strDateLine = 'Welcome ' + now.ToString('G') + '\r'; // Convert to
byte array and send. Byte[] byteDateLine =
System.Text.Encoding.ASCII.GetBytes( strDateLine.ToCharArray() );
client.Send( byteDateLine, byteDateLine.Length, 0 ); } else {
Thread.Sleep( 100 ); } } while( true ); // Don't use this. } catch(
Exception ex ) { Console.WriteLine ( ex.Message ); }
方法2 – 使用带事件的Socket
一个更为优雅的方法是创建一个事件来捕捉连接请求。ChatServer实例就采用了这种方法。首先服务器的名字和地址用下面的代码取得。
Java代码
- IPAddress [] aryLocalAddr =
null;
- string strHostName =
'';
- try
- {
- //
NOTE: DNS lookups are nice
and all but quite time
consuming.
- strHostName =
Dns.GetHostName();
- IPHostEntry ipEntry
= Dns.GetHostByName( strHostName );
- aryLocalAddr =
ipEntry.AddressList;
- }
- catch( Exception ex )
- {
- Console.WriteLine
('Error trying to get
local address {0} ',
ex.Message );
- }
-
- // Verify we got an IP
address. Tell the user if
we did
- if( aryLocalAddr ==
null ||
aryLocalAddr.Length <
1
)
- {
- Console.WriteLine(
'Unable to get local
address' );
-
return;
- }
- Console.WriteLine( 'Listening
on : [{0}] {1}',
strHostName, aryLocalAddr[0] );
IPAddress [] aryLocalAddr = null; string strHostName = ''; try { //
NOTE: DNS lookups are nice and all but quite time consuming.
strHostName = Dns.GetHostName(); IPHostEntry ipEntry =
Dns.GetHostByName( strHostName ); aryLocalAddr =
ipEntry.AddressList; } catch( Exception ex ) { Console.WriteLine
('Error trying to get local address {0} ', ex.Message ); } //
Verify we got an IP address. Tell the user if we did if(
aryLocalAdd