新浪博客

异步非阻塞的socket通信

2011-03-02 16:34阅读:
本文介绍如何使用非阻塞方式的Socket通信,并且创建了一个聊天程序的例子来帮助说明。

介绍
本文介绍如何在多个应用程序之间创建和使用TCP/IP Socket来进行通信。这些应用程序可以运行在同一台机器,也可以在局域网内,甚至也可以是跨越Internet的*。这种方法的好处是不需要你自己来使用线程,而是通过调用Socket的非阻塞模式来实现。在例子中:服务器创建病侦听客户端的连接,一旦有客户连接,服务器就将其加入到一个活动客户的列表中,某个客户端发送的消息也有服务器发送到各个连接的客户端,就好像聊天室中的那样。或许Remoting (远程调用)是做这种工作更好的办法,但是我们这里还是来学习学习如何使用Socket来实现。

*注意:跨越Internet的通讯要求服务器有独立的IP地址并且不在代理或是放火墙之后。
事件时序
服务器必须要先侦听,客户端才能够连接。下面的图例说明了在一个异步Socket会话中的事件时序。


运行示例
实例代码分为两部分:ChatServer 和ChatClient. 我们首先来创建ChatServer ,然后使用下面的Telnet命令来测试它。


C代码 复制代码

  1. telnet {server machine IP address or machine name} 399
  2. 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代码 复制代码
  1. private Socket client = null;
  2. const int nPortListen = 399;
  3. try
  4. {
  5. TcpListener listener = new TcpListener( nPortListen );
  6. Console.WriteLine( 'Listening as {0}', listener.LocalEndpoint );
  7. listener.Start();
  8. do
  9. {
  10. byte [] m_byBuff = new byte[127];
  11. if( listener.Pending() )
  12. {
  13. client = listener.AcceptSocket();
  14. // Get current date and time.
  15. DateTime now = DateTime.Now;
  16. string strDateLine = 'Welcome ' + now.ToString('G') + '\r';
  17. // Convert to byte array and send.
  18. Byte[] byteDateLine = System.Text.Encoding.ASCII.GetBytes( strDateLine.ToCharArray() );
  19. client.Send( byteDateLine, byteDateLine.Length, 0 );
  20. }
  21. else
  22. {
  23. Thread.Sleep( 100 );
  24. }
  25. } while( true ); // Don't use this.
  26. }
  27. catch( Exception ex )
  28. {
  29. Console.WriteLine ( ex.Message );
  30. }

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代码 复制代码
  1. IPAddress [] aryLocalAddr = null;
  2. string strHostName = '';
  3. try
  4. {
  5. // NOTE: DNS lookups are nice and all but quite time consuming.
  6. strHostName = Dns.GetHostName();
  7. IPHostEntry ipEntry = Dns.GetHostByName( strHostName );
  8. aryLocalAddr = ipEntry.AddressList;
  9. }
  10. catch( Exception ex )
  11. {
  12. Console.WriteLine ('Error trying to get local address {0} ', ex.Message );
  13. }
  14. // Verify we got an IP address. Tell the user if we did
  15. if( aryLocalAddr == null || aryLocalAddr.Length < 1 )
  16. {
  17. Console.WriteLine( 'Unable to get local address' );
  18. return;
  19. }
  20. 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

我的更多文章

下载客户端阅读体验更佳

APP专享