论文部分内容阅读
摘要:该系统是一个基于.NET 1.1,使用C#开发的模拟Telnet的网络客户端应用程序。本系统涉及到.net下Socket的使用、TCP/IP协议、多线程开发、并发控制、多线程下WinForm的使用、以及示例中使用的POP3协议。本系统的在Visual Studio 2003 环境下使用C#编写和生成,并在Windows XP Professional sp2下测试通过。
关键词:网络客户端应用程序;多线程;协议;并发控制
中图分类号:TP393文献标识码:A 文章编号:1009-3044(2007)17-31281-01
The Asynchronous Information Receiving and Sending System on Basis of TCP
FANG Xu-hua, YAN Hui-jia
(Zhejiang Institute of Communications, Hangzhou 311112,China)
Abstract:The system is a network customer application on the basis of .NET 1.1, developed with C# programming language. It involves the use of Socket on the basis of .net、TCP/IP protocol、multi-threading programming、concurrency control、the use of WinForm and POP3 protocol. The system is edited and compiled in Visual Studio 2003, and successfully tested in Windows XP Professional sp2.
Key words:network customer application; multi-thread; protocol; concurrency control
1 引言
网络访问是一个耗时且相对容易失败的操作,因此,基于多线程的异步的网络应用程序是当前网络应用程序实现中的主流方法。本程序主要演示了使用C#实现异步网络访问。
2 系统架构
系统运行中主要创建的线程有:主线程、创建连接的线程、数据发送线程、数据接收线程。另外,因为发送操作由Socket.BeginSend方法完成,每次调用该方法,都会在后台创建一个线程来执行发送操作,该线程为“执行单次发送操作的线程”;同理,接收操作由Socket.BeginReceive方法完成,每次调用该方法,都会在后台创建一个线程来执行接收操作,该线程为“执行单次接收操作的线程”。系统的整体架构如图1所示。
系统的实现主要由两个类完成,即类Form1和类AsynchSocketManager。类Form1是封装用户界面的类,类AsynchSocketManager完成实际的网络操作。主线程和创建连接的线程由类Form1控制,而其余的线程均由类AsynchSocketManager控制。每创建一个新的连接,都会创建类AsynchSocketManager的一个实例,也就是每个AsynchSocketManager的实例表示一个连接。Form1包含了当前AsynchSocketManager实例的引用。
类AsynchSocketManager维护一个Socket的实例。System.Net.Sockets 命名空间包含 Windows 套接字接口的托管实现。System.Net 命名空间中的所有其他网络访问类都建立在该套接字实现之上。.NET Framework Socket类是 Winsock32 API 提供的套接字服务的托管代码版本。大多数情况下,Socket类方法只是将数据封送到它们的本机 Win32 副本中并处理任何必要的安全检查。本系统使用Socket类,并使用TCP协议与服务器连接。
类AsynchSocketManager同时维护一个待发消息队列(AsynchSocketManager. TransmitQueue),参看图1,当用户界面中的发送按钮被点击后,消息并没有被直接发送到服务器,相反,该消息被添加到待发消息队列中。然后,由“数据发送线程”完成消息的发送工作。因此,待发消息队列是一个临界资源,它由主线程和数据发送线程访问,这里由一个读写锁ReaderWriterLock(AsynchSocketManager.TransmitLock)来控制对待发消息队列的并发访问。
同时,AsynchSocketManager主要暴露了Connect、Disconnect、SendMessage三个实例方法,调用方通过这三个操作完成创建连接、断开连接和发送消息三个主要功能。关于这三个函数的签名,请参见代码。
为了将执行结果反馈给用户,AsynchSocketManager同时通过回调机制,将执行结果返回给Form1。系统采用.NET中的委托来实现回调。类AsynchSocketManager的构造函数包含的三个参数OnConnect、OnDisconnect和OnReceive分别表示创建连接后、断开连接后和接收到数据后调用的委托。
图1 系统中的线程及主要方法的执行顺序图
现按线程将主要的方法调用流程分述如下:
2.1 主线程
主线程即UI线程,该线程运行用户界面,接收用户的输入。主要执行三项操作:创建连接、发送消息和断开连接。
(上接第1281页)
2.1.1 创建连接的线程
创建连接的工作过程是:创建连接由主线程发起,主线程不直接调用AsynchSocketManager的实例方法Connect,也就是主线程不直接执行创建连接的方法,而是启动一个新的工作线程“创建连接的线程”来完成创建连接的工作,图1清楚的体现了这一点。因为创建连接是一个很耗时的操作,如果在主线程中执行创建连接的操作,很可能使主线程在长时间内不响应,因此启动一个新的工作线程来执行创建连接的操作。创建连接的操作的实现,详见Form1类中的cmdConnect_Click方法和ThreadFunction方法,cmdConnect_Click方法响应用户事件并启动一个新的工作线程,ThreadFunction方法为新的工作线程的入口函数,该函数中创建了AsynchSocketManager类的实例同时对该实例调用实例方法Connect来创建连接。当连接被创建后,系统通过以下两个方法:
//通过线程池启动消息接收线程
ThreadPool.QueueUserWorkItem(
new WaitCallback(ReceiveThreadEntryPoint));
//通过线程池启动消息发送线程
ThreadPool.QueueUserWorkItem(
new WaitCallback(SendThreadEntryPoint));
启动数据发送线程和数据接收线程,创建连接的操作结束,同时创建连接的线程运行完毕退出。
2.1.2 数据发送线程
数据发送线程是一个在连接中断之前一直保持运行的线程。数据发送线程通过一个信号量StopEvent来判断是否退出。主线程通过在断开连接时设置该信号量来使数据发送线程退出。
发送消息的工作过程是:发送消息由主线程发起,主线程将消息发送到待发消息缓存中。
系统包含一个信号量DataReady,DataReady对象是AutoResetEvent类的一个实例,AutoResetEvent 允许线程通过发信号互相通信。DataReady对象被初始化为非终止状态,数据发送线程在DataReady对象处于非终止状态时被阻塞。当主线程将消息发送到待发消息缓存中后,将DataReady对象设置为终止状态,之后,处于阻塞状态的数据发送线程被释放(同时DataReady对象自动恢复非终止状态,数据发送线程在下一次对DataReady对象调用WaitOne时被阻塞)。数据发送线程遍历待发消息队列,如果发现有待发消息,则使用Socket.BeginSend方法异步发送每个消息。
2.1.3 断开连接
断开连接的工作过程是:断开连接由主线程发起,并由主线程执行,主线程调用AsynchSocketManager的实例方法Disconnect完成断开连接的操作。
2.1.4 数据接收线程
与数据发送线程类似,数据接收线程是一个在连接中断之前一直保持运行的线程。数据接收线程通过一个信号量StopEvent来判断是否退出。主线程通过在断开连接时设置该信号量来使数据接收线程退出。
数据接收线程使用Socket.BeginReceive方法异步接收消息。当消息接收完毕后,回调函数AsynchReadCallback被执行,该方法首先将接收到的消息发送给主线程来显示给用户,然后,调用Socket.BeginReceive方法异步接收下一个消息。
3 运行示例
以POP3协议为例,演示该程序的运行情况。这里选择国内最大的邮件服务供应商163的pop.163.com服务器作为试验对象,在运行该示例前,必须拥有一个163邮箱的帐号。
在服务器地址中输入pop.163.com,在服务器端口中输入110,点击连接按钮;
当连接成功后,在消息文本框中输入 user YourUserName,点击发送按钮;
在消息文本框中输入 pass YourPassword,点击发送按钮;
在消息文本框中输入 list,点击发送按钮;
(请使用有效的帐号和密码来代替YourUserName和YourPassword)
当以上的操作被成功执行后,结果应该类似图2:
图2 运行示例图
图中帐号和密码部分已被隐藏。--> 部分表示消息文本框中输入的内容,其余为服务器返回的数据。与POP服务器建立连接后依次发送user和pass命令来登录服务器,发送list命令列出收件箱中的所有邮件的列表。列表中包含了邮件编号和邮件的大小,+OK 263 78020469 表示收件箱中有263封邮件,总大小为78020469字节。
4 结束语
通过该程序的实际运行,显示了异步网络访问可以达到良好的性能和令用户满意的使用体验。该程序基本上涵盖了.net下Socket客户端编程的基本理论和操作方法。
本系统也可以被扩展为传统控制台界面的Telnet程序的一个GUI版本的替代程序。
参考文献:
[1]微软公司.Visual C# 2005程序设计语言[M]. 北京:高等教育出版社,2007.
[2]孙维煜.C#案例开发[M]. 北京:中国水利水电出版社,2005.
[3]杨宏伟, 李晶,等编著. C#程序员开发手册[M]. 北京: 科学出版社,2006.
[4]唐耀.C#程序设计实用教程[M]. 北京:中国水利水电出版社 ,2005.
[5](美)迪特尔(Deitel,H.M.)等著;葛昊晗,等译.C#大学教程[M]. 北京:清华大学出版社 ,2003.
注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。
关键词:网络客户端应用程序;多线程;协议;并发控制
中图分类号:TP393文献标识码:A 文章编号:1009-3044(2007)17-31281-01
The Asynchronous Information Receiving and Sending System on Basis of TCP
FANG Xu-hua, YAN Hui-jia
(Zhejiang Institute of Communications, Hangzhou 311112,China)
Abstract:The system is a network customer application on the basis of .NET 1.1, developed with C# programming language. It involves the use of Socket on the basis of .net、TCP/IP protocol、multi-threading programming、concurrency control、the use of WinForm and POP3 protocol. The system is edited and compiled in Visual Studio 2003, and successfully tested in Windows XP Professional sp2.
Key words:network customer application; multi-thread; protocol; concurrency control
1 引言
网络访问是一个耗时且相对容易失败的操作,因此,基于多线程的异步的网络应用程序是当前网络应用程序实现中的主流方法。本程序主要演示了使用C#实现异步网络访问。
2 系统架构
系统运行中主要创建的线程有:主线程、创建连接的线程、数据发送线程、数据接收线程。另外,因为发送操作由Socket.BeginSend方法完成,每次调用该方法,都会在后台创建一个线程来执行发送操作,该线程为“执行单次发送操作的线程”;同理,接收操作由Socket.BeginReceive方法完成,每次调用该方法,都会在后台创建一个线程来执行接收操作,该线程为“执行单次接收操作的线程”。系统的整体架构如图1所示。
系统的实现主要由两个类完成,即类Form1和类AsynchSocketManager。类Form1是封装用户界面的类,类AsynchSocketManager完成实际的网络操作。主线程和创建连接的线程由类Form1控制,而其余的线程均由类AsynchSocketManager控制。每创建一个新的连接,都会创建类AsynchSocketManager的一个实例,也就是每个AsynchSocketManager的实例表示一个连接。Form1包含了当前AsynchSocketManager实例的引用。
类AsynchSocketManager维护一个Socket的实例。System.Net.Sockets 命名空间包含 Windows 套接字接口的托管实现。System.Net 命名空间中的所有其他网络访问类都建立在该套接字实现之上。.NET Framework Socket类是 Winsock32 API 提供的套接字服务的托管代码版本。大多数情况下,Socket类方法只是将数据封送到它们的本机 Win32 副本中并处理任何必要的安全检查。本系统使用Socket类,并使用TCP协议与服务器连接。
类AsynchSocketManager同时维护一个待发消息队列(AsynchSocketManager. TransmitQueue),参看图1,当用户界面中的发送按钮被点击后,消息并没有被直接发送到服务器,相反,该消息被添加到待发消息队列中。然后,由“数据发送线程”完成消息的发送工作。因此,待发消息队列是一个临界资源,它由主线程和数据发送线程访问,这里由一个读写锁ReaderWriterLock(AsynchSocketManager.TransmitLock)来控制对待发消息队列的并发访问。
同时,AsynchSocketManager主要暴露了Connect、Disconnect、SendMessage三个实例方法,调用方通过这三个操作完成创建连接、断开连接和发送消息三个主要功能。关于这三个函数的签名,请参见代码。
为了将执行结果反馈给用户,AsynchSocketManager同时通过回调机制,将执行结果返回给Form1。系统采用.NET中的委托来实现回调。类AsynchSocketManager的构造函数包含的三个参数OnConnect、OnDisconnect和OnReceive分别表示创建连接后、断开连接后和接收到数据后调用的委托。
图1 系统中的线程及主要方法的执行顺序图
现按线程将主要的方法调用流程分述如下:
2.1 主线程
主线程即UI线程,该线程运行用户界面,接收用户的输入。主要执行三项操作:创建连接、发送消息和断开连接。
(上接第1281页)
2.1.1 创建连接的线程
创建连接的工作过程是:创建连接由主线程发起,主线程不直接调用AsynchSocketManager的实例方法Connect,也就是主线程不直接执行创建连接的方法,而是启动一个新的工作线程“创建连接的线程”来完成创建连接的工作,图1清楚的体现了这一点。因为创建连接是一个很耗时的操作,如果在主线程中执行创建连接的操作,很可能使主线程在长时间内不响应,因此启动一个新的工作线程来执行创建连接的操作。创建连接的操作的实现,详见Form1类中的cmdConnect_Click方法和ThreadFunction方法,cmdConnect_Click方法响应用户事件并启动一个新的工作线程,ThreadFunction方法为新的工作线程的入口函数,该函数中创建了AsynchSocketManager类的实例同时对该实例调用实例方法Connect来创建连接。当连接被创建后,系统通过以下两个方法:
//通过线程池启动消息接收线程
ThreadPool.QueueUserWorkItem(
new WaitCallback(ReceiveThreadEntryPoint));
//通过线程池启动消息发送线程
ThreadPool.QueueUserWorkItem(
new WaitCallback(SendThreadEntryPoint));
启动数据发送线程和数据接收线程,创建连接的操作结束,同时创建连接的线程运行完毕退出。
2.1.2 数据发送线程
数据发送线程是一个在连接中断之前一直保持运行的线程。数据发送线程通过一个信号量StopEvent来判断是否退出。主线程通过在断开连接时设置该信号量来使数据发送线程退出。
发送消息的工作过程是:发送消息由主线程发起,主线程将消息发送到待发消息缓存中。
系统包含一个信号量DataReady,DataReady对象是AutoResetEvent类的一个实例,AutoResetEvent 允许线程通过发信号互相通信。DataReady对象被初始化为非终止状态,数据发送线程在DataReady对象处于非终止状态时被阻塞。当主线程将消息发送到待发消息缓存中后,将DataReady对象设置为终止状态,之后,处于阻塞状态的数据发送线程被释放(同时DataReady对象自动恢复非终止状态,数据发送线程在下一次对DataReady对象调用WaitOne时被阻塞)。数据发送线程遍历待发消息队列,如果发现有待发消息,则使用Socket.BeginSend方法异步发送每个消息。
2.1.3 断开连接
断开连接的工作过程是:断开连接由主线程发起,并由主线程执行,主线程调用AsynchSocketManager的实例方法Disconnect完成断开连接的操作。
2.1.4 数据接收线程
与数据发送线程类似,数据接收线程是一个在连接中断之前一直保持运行的线程。数据接收线程通过一个信号量StopEvent来判断是否退出。主线程通过在断开连接时设置该信号量来使数据接收线程退出。
数据接收线程使用Socket.BeginReceive方法异步接收消息。当消息接收完毕后,回调函数AsynchReadCallback被执行,该方法首先将接收到的消息发送给主线程来显示给用户,然后,调用Socket.BeginReceive方法异步接收下一个消息。
3 运行示例
以POP3协议为例,演示该程序的运行情况。这里选择国内最大的邮件服务供应商163的pop.163.com服务器作为试验对象,在运行该示例前,必须拥有一个163邮箱的帐号。
在服务器地址中输入pop.163.com,在服务器端口中输入110,点击连接按钮;
当连接成功后,在消息文本框中输入 user YourUserName,点击发送按钮;
在消息文本框中输入 pass YourPassword,点击发送按钮;
在消息文本框中输入 list,点击发送按钮;
(请使用有效的帐号和密码来代替YourUserName和YourPassword)
当以上的操作被成功执行后,结果应该类似图2:
图2 运行示例图
图中帐号和密码部分已被隐藏。--> 部分表示消息文本框中输入的内容,其余为服务器返回的数据。与POP服务器建立连接后依次发送user和pass命令来登录服务器,发送list命令列出收件箱中的所有邮件的列表。列表中包含了邮件编号和邮件的大小,+OK 263 78020469 表示收件箱中有263封邮件,总大小为78020469字节。
4 结束语
通过该程序的实际运行,显示了异步网络访问可以达到良好的性能和令用户满意的使用体验。该程序基本上涵盖了.net下Socket客户端编程的基本理论和操作方法。
本系统也可以被扩展为传统控制台界面的Telnet程序的一个GUI版本的替代程序。
参考文献:
[1]微软公司.Visual C# 2005程序设计语言[M]. 北京:高等教育出版社,2007.
[2]孙维煜.C#案例开发[M]. 北京:中国水利水电出版社,2005.
[3]杨宏伟, 李晶,等编著. C#程序员开发手册[M]. 北京: 科学出版社,2006.
[4]唐耀.C#程序设计实用教程[M]. 北京:中国水利水电出版社 ,2005.
[5](美)迪特尔(Deitel,H.M.)等著;葛昊晗,等译.C#大学教程[M]. 北京:清华大学出版社 ,2003.
注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。