Socket Programming in C#--Getting Started
Getting Started
You can argue that one can overcome these shortcomings by multithreading meaning that one can spawn a new thread and let that thread do the polling which then notifies the main thread of the data. This concept could work well, but even if you create a new thread it would require your main thread to share the CPU time with this new thread. Windows operating system (Windows NT /2000 /XP) provide what is called Completion Port IO model for doing overlapped ( asynchronous) IO.
你可能会提出克服上述不足之处可以用多线程的方式,这意味着一个线程可以衍生出一个新的线程、让这个新线程做轮询工作通知主线程中的数据。这种理念可以很好地工作,但即使你创建一个新的线程,它需要你的主线程和这个新线程共享CPU时间。Windows操作系统(Windows NT /2000 /XP)提供了一个被称为IO异步操作模型来处理重叠(异步)IO操作。
The details of IO Completion port are beyond the scope of the current discussion, but to make it simple you can think of IO Completion Ports as the most efficient mechanism for doing asynchronous IO in Windows that is provided by the Operating system. Completion Port model can be applied to any kind of IO including the file read /write and serial communication.
IOCP模型的细节已经超出了本文讨论的范围,但是可以让你对I/O完成端口的理解更简单些,它是最有效率的处理IO异步操作的模型在windows系统中,这个模型是由操作系统提供的。I/O完成端口模型能被应用于各种类型的IO操作包括对文件的读写和串行通讯。
The .NET asynchronous socket programming helper class's Socket provides the similar model.
.Net异步socket编程辅助类提供了相似的模型。
BeginReceive
.NET framework's Socket class provides BeginReceive method to receive data asynchronously i.e., in an non-blocking manner The BeginReceive method has following signature:
public IAsyncResult BeginReceive( byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state );
.Net 框架的Socket类提供了异步接收数据的方法BeginReceive(),在非阻塞的方式下BeginReceive()方法具有以下特征:
public IAsyncResult BeginReceive( byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state );
The way BeginReceive function works is that you pass the function a buffer , a callback function (delegate) which will be called whenever data arrives.
BeginReceive()函数是通过这种方式工作的:你传递给此函数一个数据流缓冲区,当数据到达时将会调用一个回调函数(委托)。
The last parameter, object, to the BeginReceive can be any class derived from object ( even null ) . When the callback function is called it means that the BeginReceive function completed which means that the data has arrived. The callback function needs to have the following signature:
void AsyncCallback( IAsyncResult ar);
方法中的最后一个形参是Object类型的, 对于BeginReceive()可能是派生的任何类对象(即使为空)。当BeginReceive()中的回调函数被调用,意味着当接收的数据到达时BeginReceive()的功能完成了。回调函数需要具有以下签名:
void AsyncCallback( IAsyncResult ar);
As you can see the callback returns void and is passed in one parameter , IAsyncResult interface , which contains the status of the asynchronous receive operation.
正如你所看到得,AsyncCallback回调函数不返回值,只被传递了一个参数:IAsyncResult接口,它包含了异步接收操作的各种状态。
The IAsyncResult interface has several properties. The first parameter - AsyncState - is an object which is same as the last parameter that you passed to BeginReceive(). The second property is AsyncWaitHandle which we will discuss in a moment. The third property indicates whether the receive was really asynchronous or it finished synchronously. The important thing to follow here is that it not necessary for an asynchronous function to always finish asynchronously - it can complete immediately if the data is already present. Next parameter is IsComplete which indicates whether the operation has completed or not.
IAsyncResult具有一些属性。第一个参数-AsyncState-是一个object类型和你传输到BeginReceive()方法的最后一个参数一样。第二个属性是AsyncWaitHandle,这个我们一会讨论。第三个属性CompletedSynchronously表示接收是否真的异步完成的还是同步完成的。这里重要的事要遵循的是对于一个异步功能函数来说它总是以异步的方式完成-它可以立即完成如果数据已经存在。第四个参数是IsComplete是指示异步操作是否完成。
If you look at the signature of the BeginReceive again you will note that the function also returns IAsyncResult. This is interesting. Just now I said that I will talk about the second peoperty of the IAsyncResult in a moment. Now is that moment. The second parameter is called AsyncWaitHandle.
如果你看下BeginReceive()的签名你会注意到这个函数也返回IAsyncResult接口。这是有趣的。刚辞我说到我一会儿将要讨论IAsyncResult接口的第二个属性。就是现在了,第二个属性被叫做AsyncWaitHandle。
The AsyncWaitHandle is of type WaitHandle, a class defined in the System.Threading namespace. WaitHandle class encapsulates a Handle (which is a pointer to int or handle ) and provides a way to wait for that handle to become signaled. The class has several static methods like WaitOne ( which is similar to WaitForSingleObject ) WaitAll ( similar to WaitForMultipleObjects with waitAll true ) , WaitAny etc. Also there are overloads of these functions available with timeouts.
AsyncWaitHandle是WaitHandle类型的,这个类被定义在System.Threading命名空间。WaitHandle类封装了一个句柄(这是一个指向int的指针),提供了一种方式去等待该句柄被发消息。这个类包含了一些静态方法例如:WaitOne(和WaitForSingleObject相似)、WaitAll(和WaitForMultipleObjects相似,直到所有的等待都为真)、WaitAny等等。同时也重载了一些适合超时操作的函数。
Coming back to our discussion of IAsyncResult interface, the handle in AsyncWaitHandle (WaitHandle) is signalled when the receive operation completes. So if we wait on that handle infinitely we will be able to know when the receive completed. This means if we pass that WaitHandle to a different thread, the different thread can wait on that handle and can notify us of the fact that the data has arrived and so that we can read the data. So you must be wondering if we use this mechanism why would we use callback function. We won't. Thats right. If we choose to use this mechanism of the WaitHandle then the callback function parameter to the BeginReceive can be null as shown here:
回到我们讨论的IAsyncResult接口,当接收操作完成时AsyncWaitHandle (WaitHandle)的句柄被通知。所以,如果我们无限地等待句柄我们能知道什么时候接收完成。这意味,如果我们给WaitHandle传递不同的线程,不同的线程能够等待响应的句柄并能够通知我们数据已经传输过来的事实,所以我们能够读取数据了。所以你一定要问:如果我们使用上述机制的话,我们为什么还要使用回调函数。我们可以不用,这是对的。如果我们使用WaitHandle的这种异步处理机制的话,BeginReceive()函数中的形参-回调函数可以为空,如下所示:
//m_asynResult is declared of type IAsyncResult and assumming that m_socClient has made a connection.
m_asynResult = m_socClient.BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,null,null);
if ( m_asynResult.AsyncWaitHandle.WaitOne () )
{
int iRx = 0 ;
iRx = m_socClient.EndReceive (m_asynResult);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
System.String szData = new System.String(chars);
txtDataRx.Text = txtDataRx.Text + szData;
}
Even though this mechanism will work fine using multiple threads, we will for now stick to our callback mechanism where the system notifies us of the completion of asynchronous operation which is Receive in this case .
尽管使用多线程的机制的工作模式会很好, 我们将坚持我们的回调机制,在接收数据流的情况下,通过操作系统通知我们异步操作的完成。
Lets say we made the call to BeginReceive and after some time the data arrived and our callback function got called.Now question is where's the data? The data is now available in the buffer that you passed as the first parameter while making call to BeginReceive() method . In the following example the data will be available in m_DataBuffer :
BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
比方说,我们已将调用了BeginReceive()方法,经过一段时间后数据达到了,我们的回调函数被调用了。现在的问题是数据在哪里?现在缓冲区的数据是可用的,当调用BeginRecieve()方法时候你传递了第一个方法中的参数。在下面的例子中,数据在BeginReceive()方法的形参变量m_DataBuffer是可用的。
BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
But before you access the buffer you need to call EndReceive() function on the socket. The EndReceive will return the number of bytes received . Its not legal to access the buffer before calling EndReceive. To put it all together look at the following simple code:
在访问数据缓冲区之前,你需要调用套接字中提供的API函数EndReceive()。EndReceive()函数返回接收到的字节流。在调用EndReceive()函数之前,就去访问数据流是不合法的。下面将通过代码将上面讲到的知识点做下实践:
byte[] m_DataBuffer = new byte [10];
IAsyncResult m_asynResult;
public AsyncCallback pfnCallBack ;
public Socket m_socClient;
// create the socket...
public void OnConnect()
{
m_socClient = new Socket (AddressFamily.InterNetwork,SocketType.Stream ,ProtocolType.Tcp );
// get the remote IP address...
IPAddress ip = IPAddress.Parse ("10.10.120.122");
int iPortNo = 8221;
//create the end point
IPEndPoint ipEnd = new IPEndPoint (ip.Address,iPortNo);
//connect to the remote host...
m_socClient.Connect ( ipEnd );
//watch for data ( asynchronously )...
WaitForData();
}
public void WaitForData()
{
if ( pfnCallBack == null )
pfnCallBack = new AsyncCallback (OnDataReceived);
// now start to listen for any data...
m_asynResult =
m_socClient.BeginReceive (m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
}
public void OnDataReceived(IAsyncResult asyn)
{
//end receive...
int iRx = 0 ;
iRx = m_socClient.EndReceive (asyn);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
System.String szData = new System.String(chars);
WaitForData();
}
The OnConnect function makes a connection to the server and then makes a call to WaitForData. WaitForData creates the callback function and makes a call to BeginReceive passing a global buffer and the callback function. When data arrives the OnDataReceive is called and the m_socClient's EndReceive is called which returns the number of bytes received and then the data is copied over to a string and a new call is made to WaitForData which will call BeginReceive again and so on. This works fine if you have one socket in you application.
OnConnect函数实现连接到服务器,然后调用WaitForData函数。WaitForData函数创建了一个回调函数pfnCallBack、调用BeginReceive()方法的参数中传递一个全局变量的字节缓冲区并调用回调函数pfnCallBack中的方法OnDataReceived。当接受的数据到达后,OnDataReceived函数被调用、相应的客户端的EndReceive函数被调用,OnDataReceived函数实现:接收客户端发送的字节流、并把接收的数据复制到一个字符串上,接着再次调用WaitForData函数并会重新调用BeginRceive()方法等等一系列操作。如果在你的应用程序中只有一个套接字的话,这会工作的很好。
Socket Programming in C#--Getting Started的更多相关文章
- [PHP-Socket] Socket Programming in PHP
Simple Client-Server socket program in PHP Introduction Sockets are used for interprocess communicat ...
- Socket Programming in C#--Conclusion
Conclusion And that's all there is to it! Here is how our client looks like Here is how our server l ...
- Socket Programming in C#--Introduction
This is the second part of the previous article about the socket programming. In the earlier article ...
- How To: Perl TCP / UDP Socket Programming using IO::Socket::INET
http://www.thegeekstuff.com/2010/07/perl-tcp-udp-socket-programming/ In this article, let us discuss ...
- Socket programming in C on Linux | tutorial
TCP/IP socket programming This is a quick guide/tutorial to learning socket programming in C languag ...
- C Socket Programming for Linux with a Server and Client Example Code
Typically two processes communicate with each other on a single system through one of the following ...
- Python Socket Programming
本文介绍使用Python进行Socket网络编程,假设读者已经具备了基本的网络编程知识和Python的基本语法知识,本文中的代码如果没有说明则都是运行在Python 3.4下. Python的sock ...
- linux c socket programming
原文:linux c socket programming http://54min.com/post/http-client-examples-using-c.html 好文章 PPT http:/ ...
- TCP Socket Programming in Node.js
TCP Socket Programming in Node.js Posted on October 26th, 2011 under Node.jsTags: Client, node.js, S ...
随机推荐
- 在SharePoint中无代码开发InfoPath应用: 获取当前用户信息
很多种不同的场景下,会需要得到当前的用户信息,例如需要根据当前用户判断组,进而控制权限. 首先InfoPath提供了一个userName方法,来实现这个目的,不过这个方法的问题是只能获得不包含域名的用 ...
- Sharepoint学习笔记—习题系列--70-573习题解析 -(Q32-Q34)
Question 32You create a custom Web Part.You need to ensure that a custom property is visible in Edit ...
- IOS 杂笔-5(NSTimer极浅析)
1.timer都会对它的target进行retain,我们需要小心对待这个target的生命周期问题,尤其是重复性的timer. 2. timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线 ...
- IOS开发中(null)与<null>的处理
不小心在开发过程中,得到了(null)以及<null>的返回值,找了好长时间只找到了一个关于<null>的. 由于要根据返回值进行判断,做出必要反应,因此必须知道返回值所代表的 ...
- iOS设计模式简介
开闭原则: 一个模块的修改,对拓展开放而对修改关闭. 举个例子:有一个类在项目中很多地方被使用了,但是由于需求,想对这个类进行拓展,这里可以使用继承拓展出子类,可以对子类进行修改,尽量不要修改原来的类 ...
- 基于AFNetWorking 3.0封装网络请求数据的类
对于使用 AFNetworking 的朋友来说,很多朋友都是直接调用 AFNetworking的 API ,这样不太好,无法做到全工程统一配置. 最好的方式就是对网络层再封装一层,全工程不允许直接使用 ...
- 去除UITableView中多余的分割线或者隐藏cell间的分割线
一:去除tableView多余的分割线 首先,自定义一个方法 -(void)setExtraCellLineHidden: (UITableView *)tableView{ UIView *v ...
- myeclipse2013 安装 egit
myeclipse2013版本: Version: 2013 Build id: 11.0-20130401 手工安装不了,那就到市场上安装. 1.Help--->Install ...
- android中的坐标系以及获取坐标的方法
android中有两种坐标系,分别称之为Android坐标系和视图坐标系.而对应的也有一些相关的方法可以获取坐标系中的 坐标值.只有搞清楚这些区别,才能在实现的时候不至于出错或者得不到你想要的效果. ...
- Oracle查看所有用户
1.查看所有用户:select * from dba_users; select * from all_users; select * from user_users; 2.查看用户或角色系统 ...