您的位置:首页 > 运维架构

有关通信方面的知识,自己copy下来慢慢看!

2009-08-19 10:09 459 查看
是网上看到的有前通信方面的,自己copy下来留着以后慢慢看的,有兴趣的也可以瞧瞧[code]NetworkStream 类提供在阻止模式下通过 Stream 套接字发送和接收数据的方法。有关阻止与非阻止 Socket 的更多信息,请参见使用异步客户端套接字。可以在同步和异步数据传输时使用 NetworkStream 类。有关同步和异步通信的更多信息,请参见 套接字。

若要创建 NetworkStream,必须提供连接的 Socket。也可指定 NetworkStream 对所提供的 Socket 具有哪些 FileAccess 权限。默认情况下,关闭 NetworkStream 并不会关闭所提供的 Socket。如果希望 NetworkStream 具有关闭所提供的 Socket 的权限,必须将 ownsSocket 参数的值指定为 true。

将 Write 和 Read 方法用于简单的单线程同步阻止 I/O。若要使用不同的线程来处理 I/O,则请考虑使用 BeginWrite 和 EndWrite 方法,或 BeginRead 和 EndRead 方法进行通信。

NetworkStream 不支持对网络数据流的随机访问。CanSeek 属性用于指示流是否支持查找,它的值始终为 false;读取 Position 属性、读取 Length 属性或者调用 Seek 方法都会引发 NotSupportedException。
C# 复制代码
// Examples for constructors that do not specify file permission.

// Create the NetworkStream for communicating with the remote host.
NetworkStream myNetworkStream;

if (networkStreamOwnsSocket){
myNetworkStream = new NetworkStream(mySocket, true);
}
else{
myNetworkStream = new NetworkStream(mySocket);
}

Socket 类为网络通信提供了一套丰富的方法和属性。Socket 类允许您使用 ProtocolType 枚举中所列出的任何一种协议执行异步和同步数据传输。

Socket 类对异步方法遵循 .NET Framework 命名模式。例如,同步的 Receive 方法对应于异步的 BeginReceive 和 EndReceive 方法。

如果应用程序在执行期间只需要一个线程,请使用下面的方法,这些方法适用于同步操作模式。

如果当前使用的是面向连接的协议(如 TCP),则服务器可以使用 Listen 方法侦听连接。Accept 方法处理任何传入的连接请求,并返回可用于与远程主机进行数据通信的 Socket。可以使用此返回的 Socket 来调用 Send 或 Receive 方法。如果要指定本地 IP 地址和端口号,请在调用 Listen 方法之前先调用 Bind 方法。如果您希望基础服务提供程序为您分配可用端口,请使用端口号 0。如果希望连接到侦听主机,请调用 Connect 方法。若要进行数据通信,请调用 Send 或 Receive 方法。

如果当前使用的是无连接协议(如 UDP),则根本不需要侦听连接。调用 ReceiveFrom 方法可接受任何传入的数据报。使用 SendTo 方法可将数据报发送到远程主机。

若要在执行过程中使用单独的线程处理通信,请使用下面的方法,这些方法适用于异步操作模式。

如果当前使用的是面向连接的协议(如 TCP),则可使用 Socket、BeginConnect 和 EndConnect 方法来连接侦听主机。通过使用 BeginSend 和 EndSend 方法,或者使用 BeginReceive 和 EndReceive 方法,可以进行异步数据通信。可以使用 BeginAccept 和 EndAccept 处理传入的连接请求。

如果您使用的是 UDP 等无连接协议,则可以使用 BeginSendTo 和 EndSendTo 来发送数据报,而使用 BeginReceiveFrom 和 EndReceiveFrom 来接收数据报。

如果对一个套接字执行多个异步操作,它们不一定按启动时的顺序完成。

当数据发送和数据接收完成之后,可使用 Shutdown 方法来禁用 Socket。在调用 Shutdown 之后,可调用 Close 方法来释放与 Socket 关联的所有资源。

通过 Socket 类,您可以使用 SetSocketOption 方法来配置 Socket。可以使用 GetSocketOption 方法来检索这些设置。

using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;

public class GetSocket
{
private static Socket ConnectSocket(string server, int port)
{
Socket s = null;
IPHostEntry hostEntry = null;

// Get host related information.
hostEntry = Dns.GetHostEntry(server);

// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
// an exception that occurs when the host IP Address is not compatible with the address family
// (typical in the IPv6 case).
foreach(IPAddress address in hostEntry.AddressList)
{
IPEndPoint ipe = new IPEndPoint(address, port);
Socket tempSocket =
new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

tempSocket.Connect(ipe);

if(tempSocket.Connected)
{
s = tempSocket;
break;
}
else
{
continue;
}
}
return s;
}

// This method requests the home page content for the specified server.
private static string SocketSendReceive(string server, int port)
{
string request = "GET / HTTP/1.1/r/nHost: " + server +
"/r/nConnection: Close/r/n/r/n";
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
Byte[] bytesReceived = new Byte[256];

// Create a socket connection with the specified server and port.
Socket s = ConnectSocket(server, port);

if (s == null)
return ("Connection failed");

// Send request to the server.
s.Send(bytesSent, bytesSent.Length, 0);

// Receive the server home page content.
int bytes = 0;
string page = "Default HTML page on " + server + ":/r/n";

// The following will block until te page is transmitted.
do {
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
}
while (bytes > 0);

return page;
}

public static void Main(string[] args)
{
string host;
int port = 80;

if (args.Length == 0)
// If no server name is passed as argument to this program,
// use the current host name as the default.
host = Dns.GetHostName();
else
host = args[0];

string result = SocketSendReceive(host, port);
Console.WriteLine(result);
}
}
IPAddress..::.Any 字段
提供一个 IP 地址,指示服务器应侦听所有网络接口上的客户端活动。此字段为只读。
如果需要使用特定的本地终结点,请使用 Bind 方法。必须先调用 Bind,然后才能调用 Listen 方法。除非需要使用特定的本地终结点,否则不必在使用 Connect 方法之前调用 Bind。对无连接和面向连接的协议都可以使用 Bind 方法。

在调用 Bind 之前,必须首先创建打算从其进行数据通信的本地 IPEndPoint。如果您不介意分配哪个本地地址,则可以用 IPAddress..::.Any 作为地址参数创建一个 IPEndPoint,这样,基础服务提供程序将会分配最适合的网络地址。如果您有多个网络接口,这将有助于简化您的应用程序。如果您不介意使用哪个本地端口,则可以创建一个使用 0 作为端口号的 IPEndPoint。在这种情况下,服务提供程序将会分配一个可用的端口号(介于 1024 和 5000 之间)。

如果使用上面的方法,您可以通过调用 LocalEndPoint 获知所分配的本地网络地址和端口号。如果当前使用的是面向连接的协议,则直到您调用了 Connect 或 EndConnect 方法后,LocalEndPoint 才会返回本地分配的网络地址。如果当前使用的是无连接协议,则直到完成一个发送或接收操作后,才可访问该信息。
c#(Socket)异步套接字代码示例发布:游客 发布日期:2007-3-9 字体:[增加 减小] 类型:转载
异步客户端套接字示例

下面的示例程序创建一个连接到服务器的客户端。该客户端是用异步套接字生成的,因此在等待服务器返回响应时不挂起客户端应用程序的执行。该应用程序将字符串发送到服务器,然后在控制台显示该服务器返回的字符串。
C#
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
// State object for receiving data from remote device.
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient {
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient() {
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect( remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client,"This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client) {
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback( IAsyncResult ar ) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args) {
StartClient();
return 0;
}
}
异步服务器套接字示例 下面的示例程序创建一个接收来自客户端的连接请求的服务器。该服务器是用异步套接字生成的,
因此在等待来自客户端的连接时不挂起服务器应用程序的执行。该应用程序接收来自客户端的字符串,
在控制台显示该字符串,然后将该字符串回显到客户端。来自客户端的字符串必须包含字符串“<EOF>”,
以发出表示消息结尾的信号。

C#
复制代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
// State object for reading client data asynchronously
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener {
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener() {
}
public static void StartListening() {
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Bind the socket to the local endpoint and listen for incoming connections.
try {
listener.Bind(localEndPoint);
listener.Listen(100);
while (true) {
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener );
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Console.WriteLine("/nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar) {
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar) {
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer,0,bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1) {
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. /n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, content);
} else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private static void Send(Socket handler, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket handler = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args) {
StartListening();
return 0;
}
}

本文来自: 脚本之家(www.jb51.net) 详细出处参考:http://www.jb51.net/article/8049.htm

C#使用异步Socket实现TCP网络服务的C/S的通讯构架(一)基础类库部分
/////////////////////////////////////////////////////////////////////////////////////////
/*

标题:在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分

当看到.NET中TcpListener和TcpClient的时候,我非常高兴,那就是我想要的通讯模式
但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们.

下面提供了一些类,可以很好的完成Tcp的C/S通讯模式.在本文的第二部分,我将为大家介绍怎么使用它们

主要通过事件来现实整个的功能:
服务器的事件包括:

服务器满
新客户端连接
客户端关闭
接收到数据

客户端使用的事件包括:

已连接服务器
接收到数据
连接关闭

另外为了灵活的处理需求变化,还提供了编码器和报文解析器的实现方法.
注意:该类库没有经过严格的测试,如出现Bug,请发送给我,我会觉得你的整个行为是对我的鼓励和支持.

*/
//////////////////////////////////////////////////////////////////////////////////////////

/// <summary>
/// (C)2003-2005 C2217 Studio
/// 保留所有权利
///
/// 文件名称: TcpCSFramework.cs
/// 文件ID:
/// 编程语言: C#
/// 文件说明: 提供TCP网络服务的C/S的通讯构架基础类
/// (使用异步Socket编程实现)
///
/// 当前版本: 1.1
/// 替换版本: 1.0
///
/// 作者: 邓杨均
/// EMail: dyj057@gmail.com
/// 创建日期: 2005-3-9
/// 最后修改日期: 2005-3-17
///
/// 历史修改记录:
///
/// 时间: 2005-3-14
/// 修改内容:
/// 1.创建Ibms.Net.TcpCSFramework命名空间和添加Session对象.
/// 2.修改NetEventArgs类,以适应新添加对象.
/// 3.添加了会话退出类型,更适合实际的情况.
/// 注意:
/// * 强制退出类型是应用程序直接结束,比如通过任务管理器结束
/// 程序或者程序异常退出等,没有执行正常的退出方法而产生的.
/// * 正常的退出类型是应用程序执行正常的退出的方法关键在于
/// 需要调用Socket.Shutdown( SocketShutdown.Both )后才调用
/// Socket.Close()方法,而不是直接的调用Socket.Close()方法,
/// 如果那样调用将产生强制退出类型.
///
/// 时间: 2005-3-16
/// 修改内容:
/// 1.创建TcpCli,Coder,DatagramResover对象,把抽象和实现部分分离
/// 2.文件版本修改为1.1,1.0版本仍然保留,更名为:
/// TcpCSFramework_v1.0.cs
/// 3.在TcpServer中修改自定义的hashtable为系统Hashtable类型
///
/// </summary>

using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Collections;

namespace Ibms.Net.TcpCSFramework
{

/// <summary>
/// 网络通讯事件模型委托
/// </summary>
public delegate void NetEvent(object sender, NetEventArgs e);

/// <summary>
/// 提供TCP连接服务的服务器类
///
/// 版本: 1.1
/// 替换版本: 1.0
///
/// 特点:
/// 1.使用hash表保存所有已连接客户端的状态,收到数据时能实现快速查找.每当
/// 有一个新的客户端连接就会产生一个新的会话(Session).该Session代表了客
/// 户端对象.
/// 2.使用异步的Socket事件作为基础,完成网络通讯功能.
/// 3.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网
/// 络环境.初步规定该类支持的最大数据报文为640K(即一个数据包的大小不能大于
/// 640K,否则服务器程序会自动删除报文数据,认为是非法数据),防止因为数据报文
/// 无限制的增长而倒是服务器崩溃
/// 4.通讯格式默认使用Encoding.Default格式这样就可以和以前32位程序的客户端
/// 通讯.也可以使用U-16和U-8的的通讯方式进行.可以在该DatagramResolver类的
/// 继承类中重载编码和解码函数,自定义加密格式进行通讯.总之确保客户端与服务
/// 器端使用相同的通讯格式
/// 5.使用C# native code,将来出于效率的考虑可以将C++代码写成的32位dll来代替
/// C#核心代码, 但这样做缺乏可移植性,而且是Unsafe代码(该类的C++代码也存在)
/// 6.可以限制服务器的最大登陆客户端数目
/// 7.比使用TcpListener提供更加精细的控制和更加强大异步数据传输的功能,可作为
/// TcpListener的替代类
/// 8.使用异步通讯模式,完全不用担心通讯阻塞和线程问题,无须考虑通讯的细节
///
/// 注意:
/// 1.部分的代码由Rational XDE生成,可能与编码规范不符
///
/// 原理:
///
///
/// 使用用法:
///
/// 例子:
///
/// </summary>
public class TcpSvr
{
#region 定义字段

/// <summary>
/// 默认的服务器最大连接客户端端数据
/// </summary>
public const int DefaultMaxClient=100;

/// <summary>
/// 接收数据缓冲区大小64K
/// </summary>
public const int DefaultBufferSize = 64*1024;

/// <summary>
/// 最大数据报文大小
/// </summary>
public const int MaxDatagramSize = 640*1024;

/// <summary>
/// 报文解析器
/// </summary>
private DatagramResolver _resolver;

/// <summary>
/// 通讯格式编码解码器
/// </summary>
private Coder _coder;

/// <summary>
/// 服务器程序使用的端口
/// </summary>
private ushort _port;

/// <summary>
/// 服务器程序允许的最大客户端连接数
/// </summary>
private ushort _maxClient;

/// <summary>
/// 服务器的运行状态
/// </summary>
private bool _isRun;

/// <summary>
/// 接收数据缓冲区
/// </summary>
private byte[] _recvDataBuffer;

/// <summary>
/// 服务器使用的异步Socket类,
/// </summary>
private Socket _svrSock;

/// <summary>
/// 保存所有客户端会话的哈希表
/// </summary>
private Hashtable _sessionTable;

/// <summary>
/// 当前的连接的客户端数
/// </summary>
private ushort _clientCount;

#endregion

#region 事件定义

/// <summary>
/// 客户端建立连接事件
/// </summary>
public event NetEvent ClientConn;

/// <summary>
/// 客户端关闭事件
/// </summary>
public event NetEvent ClientClose;

/// <summary>
/// 服务器已经满事件
/// </summary>
public event NetEvent ServerFull;

/// <summary>
/// 服务器接收到数据事件
/// </summary>
public event NetEvent RecvData;

#endregion

#region 构造函数

/// <summary>
/// 构造函数
/// </summary>
/// <param name="port">服务器端监听的端口号</param>
/// <param name="maxClient">服务器能容纳客户端的最大能力</param>
/// <param name="encodingMothord">通讯的编码方式</param>
public TcpSvr( ushort port,ushort maxClient, Coder coder)
{
_port = port;
_maxClient = maxClient;
_coder = coder;
}

/// <summary>
/// 构造函数(默认使用Default编码方式)
/// </summary>
/// <param name="port">服务器端监听的端口号</param>
/// <param name="maxClient">服务器能容纳客户端的最大能力</param>
public TcpSvr( ushort port,ushort maxClient)
{
_port = port;
_maxClient = maxClient;
_coder = new Coder(Coder.EncodingMothord.Default);
}

// <summary>
/// 构造函数(默认使用Default编码方式和DefaultMaxClient(100)个客户端的容量)
/// </summary>
/// <param name="port">服务器端监听的端口号</param>
public TcpSvr( ushort port):this( port, DefaultMaxClient)
{
}

#endregion

#region 属性

/// <summary>
/// 服务器的Socket对象
/// </summary>
public Socket ServerSocket
{
get
{
return _svrSock;
}
}

/// <summary>
/// 数据报文分析器
/// </summary>
public DatagramResolver Resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}

/// <summary>
/// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改
/// </summary>
public Hashtable SessionTable
{
get
{
return _sessionTable;
}
}

/// <summary>
/// 服务器可以容纳客户端的最大能力
/// </summary>
public int Capacity
{
get
{
return _maxClient;
}
}

/// <summary>
/// 当前的客户端连接数
/// </summary>
public int SessionCount
{
get
{
return _clientCount;
}
}

/// <summary>
/// 服务器运行状态
/// </summary>
public bool IsRun
{
get
{
return _isRun;
}

}

#endregion

#region 公有方法

/// <summary>
/// 启动服务器程序,开始监听客户端请求
/// </summary>
public virtual void Start()
{
if( _isRun )
{
throw (new ApplicationException("TcpSvr已经在运行."));
}

_sessionTable = new Hashtable(53);

_recvDataBuffer = new byte[DefaultBufferSize];

//初始化socket
_svrSock = new Socket( AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );

//绑定端口
IPEndPoint iep = new IPEndPoint( IPAddress.Any, _port);
_svrSock.Bind(iep);

//开始监听
_svrSock.Listen(5);

//设置异步方法接受客户端连接
_svrSock.BeginAccept(new AsyncCallback( AcceptConn ), _svrSock);

_isRun = true;

}

/// <summary>
/// 停止服务器程序,所有与客户端的连接将关闭
/// </summary>
public virtual void Stop()
{
if( !_isRun )
{
throw (new ApplicationException("TcpSvr已经停止"));
}

//这个条件语句,一定要在关闭所有客户端以前调用
//否则在EndConn会出现错误
_isRun = false;

//关闭数据连接,负责客户端会认为是强制关闭连接
if( _svrSock.Connected )
{
_svrSock.Shutdown( SocketShutdown.Both );
}

CloseAllClient();

//清理资源
_svrSock.Close();

_sessionTable = null;

}

/// <summary>
/// 关闭所有的客户端会话,与所有的客户端连接会断开
/// </summary>
public virtual void CloseAllClient()
{
foreach(Session client in _sessionTable.Values)
{
client.Close();
}

_sessionTable.Clear();
}

/// <summary>
/// 关闭一个与客户端之间的会话
/// </summary>
/// <param name="closeClient">需要关闭的客户端会话对象</param>
public virtual void CloseSession(Session closeClient)
{
Debug.Assert( closeClient !=null);

if( closeClient !=null )
{

closeClient.Datagram =null;

_sessionTable.Remove(closeClient.ID);

_clientCount--;

//客户端强制关闭链接
if( ClientClose != null )
{
ClientClose(this, new NetEventArgs( closeClient ));
}

closeClient.Close();
}
}

/// <summary>
/// 发送数据
/// </summary>
/// <param name="recvDataClient">接收数据的客户端会话</param>
/// <param name="datagram">数据报文</param>
public virtual void Send( Session recvDataClient, string datagram )
{
//获得数据编码
byte [] data = _coder.GetEncodingBytes(datagram);

recvDataClient.ClientSocket.BeginSend( data, 0, data.Length, SocketFlags.None,
new AsyncCallback( SendDataEnd ), recvDataClient.ClientSocket );

}

#endregion

#region 受保护方法
/// <summary>
/// 关闭一个客户端Socket,首先需要关闭Session
/// </summary>
/// <param name="client">目标Socket对象</param>
/// <param name="exitType">客户端退出的类型</param>
protected virtual void CloseClient( Socket client, Session.ExitType exitType)
{
Debug.Assert ( client !=null);

//查找该客户端是否存在,如果不存在,抛出异常
Session closeClient = FindSession(client);

closeClient.TypeOfExit = exitType;

if(closeClient!=null)
{
CloseSession(closeClient);
}
else
{
throw( new ApplicationException("需要关闭的Socket对象不存在"));
}
}

/// <summary>
/// 客户端连接处理函数
/// </summary>
/// <param name="iar">欲建立服务器连接的Socket对象</param>
protected virtual void AcceptConn(IAsyncResult iar)
{
//如果服务器停止了服务,就不能再接收新的客户端
if( !_isRun)
{
return;
}

//接受一个客户端的连接请求
Socket oldserver = ( Socket ) iar.AsyncState;

Socket client = oldserver.EndAccept(iar);

//检查是否达到最大的允许的客户端数目
if( _clientCount == _maxClient )
{
//服务器已满,发出通知
if( ServerFull != null )
{
ServerFull(this, new NetEventArgs( new Session(client)));
}

}
else
{

Session newSession = new Session( client );

_sessionTable.Add(newSession.ID, newSession);

//客户端引用计数+1
_clientCount ++;

//开始接受来自该客户端的数据
client.BeginReceive( _recvDataBuffer,0 , _recvDataBuffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveData), client);

//新的客户段连接,发出通知
if( ClientConn != null )
{
ClientConn(this, new NetEventArgs(newSession ) );
}
}

//继续接受客户端
_svrSock.BeginAccept(new AsyncCallback( AcceptConn ), _svrSock);
}

/// <summary>
/// 通过Socket对象查找Session对象
/// </summary>
/// <param name="client"></param>
/// <returns>找到的Session对象,如果为null,说明并不存在该回话</returns>
private Session FindSession( Socket client )
{
SessionId id = new SessionId((int)client.Handle);

return (Session)_sessionTable[id];
}

/// <summary>
/// 接受数据完成处理函数,异步的特性就体现在这个函数中,
/// 收到数据后,会自动解析为字符串报文
/// </summary>
/// <param name="iar">目标客户端Socket</param>
protected virtual void ReceiveData(IAsyncResult iar)
{
Socket client = (Socket)iar.AsyncState;

try
{
//如果两次开始了异步的接收,所以当客户端退出的时候
//会两次执行EndReceive

int recv = client.EndReceive(iar);

if( recv == 0 )
{
//正常的关闭
CloseClient(client, Session.ExitType.NormalExit);
return;
}

string receivedData = _coder.GetEncodingString( _recvDataBuffer, recv );

//发布收到数据的事件
if(RecvData!=null)
{
Session sendDataSession= FindSession(client);

Debug.Assert( sendDataSession!=null );

//如果定义了报文的尾标记,需要处理报文的多种情况
if(_resolver != null)
{
if( sendDataSession.Datagram !=null &&
sendDataSession.Datagram.Length !=0)
{
//加上最后一次通讯剩余的报文片断
receivedData= sendDataSession.Datagram + receivedData ;
}

string [] recvDatagrams = _resolver.Resolve(ref receivedData);

foreach(string newDatagram in recvDatagrams)
{
//深拷贝,为了保持Datagram的对立性
ICloneable copySession = (ICloneable)sendDataSession;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = newDatagram;
//发布一个报文消息
RecvData(this,new NetEventArgs( clientSession ));
}

//剩余的代码片断,下次接收的时候使用
sendDataSession.Datagram = receivedData;

if( sendDataSession.Datagram.Length > MaxDatagramSize )
{
sendDataSession.Datagram = null;
}

}
//没有定义报文的尾标记,直接交给消息订阅者使用
else
{
ICloneable copySession = (ICloneable)sendDataSession;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = receivedData;

RecvData(this,new NetEventArgs( clientSession ));
}

}//end of if(RecvData!=null)

//继续接收来自来客户端的数据
client.BeginReceive( _recvDataBuffer, 0, _recvDataBuffer.Length , SocketFlags.None,
new AsyncCallback( ReceiveData ), client);

}
catch(SocketException ex)
{
//客户端退出
if( 10054 == ex.ErrorCode )
{
//客户端强制关闭
CloseClient(client, Session.ExitType.ExceptionExit);
}

}
catch(ObjectDisposedException ex)
{
//这里的实现不够优雅
//当调用CloseSession()时,会结束数据接收,但是数据接收
//处理中会调用int recv = client.EndReceive(iar);
//就访问了CloseSession()已经处置的对象
//我想这样的实现方法也是无伤大雅的.
if(ex!=null)
{
ex=null;
//DoNothing;
}
}

}

/// <summary>
/// 发送数据完成处理函数
/// </summary>
/// <param name="iar">目标客户端Socket</param>
protected virtual void SendDataEnd(IAsyncResult iar)
{
Socket client = (Socket)iar.AsyncState;

int sent = client.EndSend(iar);
}

#endregion

}

/// <summary>
/// 提供Tcp网络连接服务的客户端类
///
/// 版本: 1.0
/// 替换版本:
///
/// 特征:
/// 原理:
/// 1.使用异步Socket通讯与服务器按照一定的通讯格式通讯,请注意与服务器的通
/// 讯格式一定要一致,否则可能造成服务器程序崩溃,整个问题没有克服,怎么从byte[]
/// 判断它的编码格式
/// 2.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网
/// 络环境.
/// 用法:
/// 注意:
/// </summary>
public class TcpCli
{
#region 字段

/// <summary>
/// 客户端与服务器之间的会话类
/// </summary>
private Session _session;

/// <summary>
/// 客户端是否已经连接服务器
/// </summary>
private bool _isConnected = false;

/// <summary>
/// 接收数据缓冲区大小64K
/// </summary>
public const int DefaultBufferSize = 64*1024;

/// <summary>
/// 报文解析器
/// </summary>
private DatagramResolver _resolver;

/// <summary>
/// 通讯格式编码解码器
/// </summary>
private Coder _coder;

/// <summary>
/// 接收数据缓冲区
/// </summary>
private byte[] _recvDataBuffer = new byte[DefaultBufferSize];

#endregion

#region 事件定义

//需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅

/// <summary>
/// 已经连接服务器事件
/// </summary>
public event NetEvent ConnectedServer;

/// <summary>
/// 接收到数据报文事件
/// </summary>
public event NetEvent ReceivedDatagram;

/// <summary>
/// 连接断开事件
/// </summary>
public event NetEvent DisConnectedServer;
#endregion

#region 属性

/// <summary>
/// 返回客户端与服务器之间的会话对象
/// </summary>
public Session ClientSession
{
get
{
return _session;
}
}

/// <summary>
/// 返回客户端与服务器之间的连接状态
/// </summary>
public bool IsConnected
{
get
{
return _isConnected;
}
}

/// <summary>
/// 数据报文分析器
/// </summary>
public DatagramResolver Resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}

/// <summary>
/// 编码解码器
/// </summary>
public Coder ServerCoder
{
get
{
return _coder;
}
}

#endregion

#region 公有方法

/// <summary>
/// 默认构造函数,使用默认的编码格式
/// </summary>
public TcpCli()
{
_coder = new Coder( Coder.EncodingMothord.Default );
}

/// <summary>
/// 构造函数,使用一个特定的编码器来初始化
/// </summary>
/// <param name="_coder">报文编码器</param>
public TcpCli( Coder coder )
{
_coder = coder;
}

/// <summary>
/// 连接服务器
/// </summary>
/// <param name="ip">服务器IP地址</param>
/// <param name="port">服务器端口</param>
public virtual void Connect( string ip, int port)
{
if(IsConnected)
{
//重新连接
Debug.Assert( _session !=null);

Close();
}

Socket newsock= new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);

IPEndPoint iep = new IPEndPoint( IPAddress.Parse(ip), port);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);

}

/// <summary>
/// 发送数据报文
/// </summary>
/// <param name="datagram"></param>
public virtual void Send( string datagram)
{
if(datagram.Length ==0 )
{
return;
}

if( !_isConnected )
{
throw (new ApplicationException("没有连接服务器,不能发送数据") );
}

//获得报文的编码字节
byte [] data = _coder.GetEncodingBytes(datagram);

_session.ClientSocket.BeginSend( data, 0, data.Length, SocketFlags.None,
new AsyncCallback( SendDataEnd ), _session.ClientSocket);
}

/// <summary>
/// 关闭连接
/// </summary>
public virtual void Close()
{
if(!_isConnected)
{
return;
}

_session.Close();

_session = null;

_isConnected = false;
}

#endregion

#region 受保护方法

/// <summary>
/// 数据发送完成处理函数
/// </summary>
/// <param name="iar"></param>
protected virtual void SendDataEnd(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
Debug.Assert(sent !=0);

}

/// <summary>
/// 建立Tcp连接后处理过程
/// </summary>
/// <param name="iar">异步Socket</param>
protected virtual void Connected(IAsyncResult iar)
{
Socket socket = (Socket)iar.AsyncState;

socket.EndConnect(iar);

//创建新的会话
_session = new Session(socket);

_isConnected = true;

//触发连接建立事件
if(ConnectedServer != null)
{
ConnectedServer(this, new NetEventArgs(_session));
}

//建立连接后应该立即接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0,
DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), socket);
}

/// <summary>
/// 数据接收处理函数
/// </summary>
/// <param name="iar">异步Socket</param>
protected virtual void RecvData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;

try
{
int recv = remote.EndReceive(iar);

//正常的退出
if(recv ==0 )
{
_session.TypeOfExit = Session.ExitType.NormalExit;

if(DisConnectedServer!=null)
{
DisConnectedServer(this, new NetEventArgs(_session));
}

return;
}

string receivedData = _coder.GetEncodingString( _recvDataBuffer,recv );

//通过事件发布收到的报文
if(ReceivedDatagram != null)
{
//通过报文解析器分析出报文
//如果定义了报文的尾标记,需要处理报文的多种情况
if(_resolver != null)
{
if( _session.Datagram !=null &&
_session.Datagram.Length !=0)
{
//加上最后一次通讯剩余的报文片断
receivedData= _session.Datagram + receivedData ;
}

string [] recvDatagrams = _resolver.Resolve(ref receivedData);

foreach(string newDatagram in recvDatagrams)
{
//Need Deep Copy.因为需要保证多个不同报文独立存在
ICloneable copySession = (ICloneable)_session;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = newDatagram;

//发布一个报文消息
ReceivedDatagram(this,new NetEventArgs( clientSession ));
}

//剩余的代码片断,下次接收的时候使用
_session.Datagram = receivedData;
}
//没有定义报文的尾标记,直接交给消息订阅者使用
else
{
ICloneable copySession = (ICloneable)_session;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = receivedData;

ReceivedDatagram( this, new NetEventArgs( clientSession ));

}

}//end of if(ReceivedDatagram != null)

//继续接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), _session.ClientSocket);
}
catch(SocketException ex)
{
//客户端退出
if( 10054 == ex.ErrorCode )
{
//服务器强制的关闭连接,强制退出
_session.TypeOfExit = Session.ExitType.ExceptionExit;

if(DisConnectedServer!=null)
{
DisConnectedServer(this, new NetEventArgs(_session));
}
}
else
{
throw( ex );
}
}
catch(ObjectDisposedException ex)
{
//这里的实现不够优雅
//当调用CloseSession()时,会结束数据接收,但是数据接收
//处理中会调用int recv = client.EndReceive(iar);
//就访问了CloseSession()已经处置的对象
//我想这样的实现方法也是无伤大雅的.
if(ex!=null)
{
ex =null;
//DoNothing;
}
}

}

#endregion

}

/// <summary>
/// 通讯编码格式提供者,为通讯服务提供编码和解码服务
/// 你可以在继承类中定制自己的编码方式如:数据加密传输等
/// </summary>
public class Coder
{
/// <summary>
/// 编码方式
/// </summary>
private EncodingMothord _encodingMothord;

protected Coder()
{

}

public Coder(EncodingMothord encodingMothord)
{
_encodingMothord = encodingMothord;
}

public enum EncodingMothord
{
Default =0,
Unicode,
UTF8,
ASCII,
}

/// <summary>
/// 通讯数据解码
/// </summary>
/// <param name="dataBytes">需要解码的数据</param>
/// <returns>编码后的数据</returns>
public virtual string GetEncodingString( byte [] dataBytes,int size)
{
switch( _encodingMothord )
{
case EncodingMothord.Default:
{
return Encoding.Default.GetString(dataBytes,0,size);
}
case EncodingMothord.Unicode:
{
return Encoding.Unicode.GetString(dataBytes,0,size);
}
case EncodingMothord.UTF8:
{
return Encoding.UTF8.GetString(dataBytes,0,size);
}
case EncodingMothord.ASCII:
{
return Encoding.ASCII.GetString(dataBytes,0,size);
}
default:
{
throw( new Exception("未定义的编码格式"));
}
}

}

/// <summary>
/// 数据编码
/// </summary>
/// <param name="datagram">需要编码的报文</param>
/// <returns>编码后的数据</returns>
public virtual byte[] GetEncodingBytes(string datagram)
{
switch( _encodingMothord)
{
case EncodingMothord.Default:
{
return Encoding.Default.GetBytes(datagram);
}
case EncodingMothord.Unicode:
{
return Encoding.Unicode.GetBytes(datagram);
}
case EncodingMothord.UTF8:
{
return Encoding.UTF8.GetBytes(datagram);
}
case EncodingMothord.ASCII:
{
return Encoding.ASCII.GetBytes(datagram);
}
default:
{
throw( new Exception("未定义的编码格式"));
}
}
}

}

/// <summary>
/// 数据报文分析器,通过分析接收到的原始数据,得到完整的数据报文.
/// 继承该类可以实现自己的报文解析方法.
/// 通常的报文识别方法包括:固定长度,长度标记,标记符等方法
/// 本类的现实的是标记符的方法,你可以在继承类中实现其他的方法
/// </summary>
public class DatagramResolver
{
/// <summary>
/// 报文结束标记
/// </summary>
private string endTag;

/// <summary>
/// 返回结束标记
/// </summary>
string EndTag
{
get
{
return endTag;
}
}

/// <summary>
/// 受保护的默认构造函数,提供给继承类使用
/// </summary>
protected DatagramResolver()
{

}

/// <summary>
/// 构造函数
/// </summary>
/// <param name="endTag">报文结束标记</param>
public DatagramResolver(string endTag)
{
if(endTag == null)
{
throw (new ArgumentNullException("结束标记不能为null"));
}

if(endTag == "")
{
throw (new ArgumentException("结束标记符号不能为空字符串"));
}

this.endTag = endTag;
}

/// <summary>
/// 解析报文
/// </summary>
/// <param name="rawDatagram">原始数据,返回未使用的报文片断,
/// 该片断会保存在Session的Datagram对象中</param>
/// <returns>报文数组,原始数据可能包含多个报文</returns>
public virtual string [] Resolve(ref string rawDatagram)
{
ArrayList datagrams = new ArrayList();

//末尾标记位置索引
int tagIndex =-1;

while(true)
{
tagIndex = rawDatagram.IndexOf(endTag,tagIndex+1);

if( tagIndex == -1 )
{
break;
}
else
{
//按照末尾标记把字符串分为左右两个部分
string newDatagram = rawDatagram.Substring(
0, tagIndex+endTag.Length);

datagrams.Add(newDatagram);

if(tagIndex+endTag.Length >= rawDatagram.Length)
{
rawDatagram="";

break;
}

rawDatagram = rawDatagram.Substring(tagIndex+endTag.Length,
rawDatagram.Length - newDatagram.Length);

//从开始位置开始查找
tagIndex=0;
}
}

string [] results= new string[datagrams.Count];

datagrams.CopyTo(results);

return results;
}

}

/// <summary>
/// 客户端与服务器之间的会话类
///
/// 版本: 1.1
/// 替换版本: 1.0
///
/// 说明:
/// 会话类包含远程通讯端的状态,这些状态包括Socket,报文内容,
/// 客户端退出的类型(正常关闭,强制退出两种类型)
/// </summary>
public class Session:ICloneable
{
#region 字段

/// <summary>
/// 会话ID
/// </summary>
private SessionId _id;

/// <summary>
/// 客户端发送到服务器的报文
/// 注意:在有些情况下报文可能只是报文的片断而不完整
/// </summary>
private string _datagram;

/// <summary>
/// 客户端的Socket
/// </summary>
private Socket _cliSock;

/// <summary>
/// 客户端的退出类型
/// </summary>
private ExitType _exitType;

/// <summary>
/// 退出类型枚举
/// </summary>
public enum ExitType
{
NormalExit ,
ExceptionExit
};

#endregion

#region 属性

/// <summary>
/// 返回会话的ID
/// </summary>
public SessionId ID
{
get
{
return _id;
}
}

/// <summary>
/// 存取会话的报文
/// </summary>
public string Datagram
{
get
{
return _datagram;
}
set
{
_datagram = value;
}
}

/// <summary>
/// 获得与客户端会话关联的Socket对象
/// </summary>
public Socket ClientSocket
{
get
{
return _cliSock;
}
}

/// <summary>
/// 存取客户端的退出方式
/// </summary>
public ExitType TypeOfExit
{
get
{
return _exitType;
}

set
{
_exitType = value;
}
}

#endregion

#region 方法

/// <summary>
/// 使用Socket对象的Handle值作为HashCode,它具有良好的线性特征.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return (int)_cliSock.Handle;
}

/// <summary>
/// 返回两个Session是否代表同一个客户端
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
Session rightObj = (Session)obj;

return (int)_cliSock.Handle == (int)rightObj.ClientSocket.Handle;

}

/// <summary>
/// 重载ToString()方法,返回Session对象的特征
/// </summary>
/// <returns></returns>
public override string ToString()
{
string result = string.Format("Session:{0},IP:{1}",
_id,_cliSock.RemoteEndPoint.ToString());

//result.C
return result;
}

/// <summary>
/// 构造函数
/// </summary>
/// <param name="cliSock">会话使用的Socket连接</param>
public Session( Socket cliSock)
{
Debug.Assert( cliSock !=null );

_cliSock = cliSock;

_id = new SessionId( (int)cliSock.Handle);
}

/// <summary>
/// 关闭会话
/// </summary>
public void Close()
{
Debug.Assert( _cliSock !=null );

//关闭数据的接受和发送
_cliSock.Shutdown( SocketShutdown.Both );

//清理资源
_cliSock.Close();
}

#endregion

#region ICloneable 成员

object System.ICloneable.Clone()
{
Session newSession = new Session(_cliSock);
newSession.Datagram = _datagram;
newSession.TypeOfExit = _exitType;

return newSession;
}

#endregion
}

/// <summary>
/// 唯一的标志一个Session,辅助Session对象在Hash表中完成特定功能
/// </summary>
public class SessionId
{
/// <summary>
/// 与Session对象的Socket对象的Handle值相同,必须用这个值来初始化它
/// </summary>
private int _id;

/// <summary>
/// 返回ID值
/// </summary>
public int ID
{
get
{
return _id;
}
}

/// <summary>
/// 构造函数
/// </summary>
/// <param name="id">Socket的Handle值</param>
public SessionId(int id)
{
_id = id;
}

/// <summary>
/// 重载.为了符合Hashtable键值特征
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if(obj != null )
{
SessionId right = (SessionId) obj;

return _id == right._id;
}
else if(this == null)
{
return true;
}
else
{
return false;
}

}

/// <summary>
/// 重载.为了符合Hashtable键值特征
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return _id;
}

/// <summary>
/// 重载,为了方便显示输出
/// </summary>
/// <returns></returns>
public override string ToString()
{
return _id.ToString ();
}

}

/// <summary>
/// 服务器程序的事件参数,包含了激发该事件的会话对象
/// </summary>
public class NetEventArgs:EventArgs
{

#region 字段

/// <summary>
/// 客户端与服务器之间的会话
/// </summary>
private Session _client;

#endregion

#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="client">客户端会话</param>
public NetEventArgs(Session client)
{
if( null == client)
{
throw(new ArgumentNullException());
}

_client = client;
}
#endregion

#region 属性

/// <summary>
/// 获得激发该事件的会话对象
/// </summary>
public Session Client
{
get
{
return _client;
}

}

#endregion

}
}

C#使用异步Socket实现TCP网络服务的C/S的通讯构架(二)使用方法

一.TcpSvr的使用方法
A.测试程序:
using System;
using Ibms.Net.TcpCSFramework;
using System.Collections;
using System.Net.Sockets;

namespace Ibms.Test
{
/// <summary>
/// 测试TcpSvr的类
/// </summary>
public class TestTcpSvr
{

public TestTcpSvr()
{

}

public static void Main()
{
try
{

Console.WriteLine("Begin to Test TcpSvr class...");

TestTcpSvr tts = new TestTcpSvr();

//TcpSvr svr = new TcpSvr(9050,4);//默认使用Encoding.Default编码方式
TcpSvr svr = new TcpSvr(9050,4,new Coder(Coder.EncodingMothord.UTF8));

svr.Resovlver = new DatagramResolver("##");

//定义服务器的4个事件

//服务器满
svr.ServerFull += new NetEvent(tts.ServerFull);

//新客户端连接
svr.ClientConn += new NetEvent(tts.ClientConn);

//客户端关闭
svr.ClientClose += new NetEvent(tts.ClientClose);

//接收到数据
svr.RecvData += new NetEvent(tts.RecvData);

//命令控制循环
while(true)
{
Console.Write(">");

string cmd=Console.ReadLine();

//退出测试程序
if(cmd.ToLower() == "exit")
{
break;
}

//停止服务器程序
if(cmd.ToLower() == "stop")
{
svr.Stop();

Console.WriteLine("Server is Stop.");

continue;
}

//运行服务器程序
if(cmd.ToLower() == "start")
{
svr.Start();

Console.WriteLine("Server is listen...{0}",
svr.ServerSocket.LocalEndPoint.ToString());

continue;
}

//察看服务器在线客户端数目和容量
if(cmd.ToLower() == "count")
{
Console.WriteLine("Current count of Client is {0}/{1}",
svr.SessionCount,svr.Capacity);
continue;
}

//发送数据到客户端格式:send [Session] [stringData]
if(cmd.ToLower().IndexOf("send") !=-1)
{
cmd = cmd.ToLower();

string[] para = cmd.Split(' ');

if(para.Length ==3)
{

Session client = (Session)svr.SessionTable[ new SessionId( int.Parse
(para[1]))];

if(client !=null)
{
svr.Send(client, para[2]);
}
else
{
Console.WriteLine("The Session is Null");
}

}
else
{
Console.WriteLine("Error Command");
}

continue;
}

//从服务器上踢掉一个客户端
if(cmd.ToLower().IndexOf("kick") !=-1)
{
cmd = cmd.ToLower();

string[] para = cmd.Split(' ');

if(para.Length ==2)
{
Session client = (Session)svr.SessionTable[ new SessionId( int.Parse
(para[1]))];

if(client !=null)
{
svr.CloseSession(client);
}
else
{
Console.WriteLine("The Session is Null");
}
}
else
{
Console.WriteLine("Error command");
}

continue;

}

//列出服务器上所有的客户端信息
if(cmd.ToLower() == "list")
{
int i=0;

foreach( Session Client in svr.SessionTable.Values)
{
if(Client !=null)
{
i++;
string info = string.Format("{0} Client:{1} connected server Session:{2}. Socket Handle:{3}",
i,
Client.ClientSocket.RemoteEndPoint.ToString(),
Client.ID,
Client.ClientSocket.Handle);

Console.WriteLine( info );
}
else
{
i++;

string info = string.Format("{0} null Client", i);
Console.WriteLine(info);

}
}

continue;

}

Console.WriteLine("Unkown Command");

}//end of while

Console.WriteLine("End service");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}

}

void ClientConn(object sender, NetEventArgs e)
{
string info = string.Format("A Client:{0} connect server Session:{1}. Socket Handle:{2}",
e.Client.ClientSocket.RemoteEndPoint.ToString(),
e.Client.ID,e.Client.ClientSocket.Handle);

Console.WriteLine( info );

Console.Write(">");
}

void ServerFull(object sender, NetEventArgs e)
{
string info = string.Format("Server is full.the Client:{0} is refused",
e.Client.ClientSocket.RemoteEndPoint.ToString());

//Must do it
//服务器满了,必须关闭新来的客户端连接
e.Client.Close();

Console.WriteLine(info);

Console.Write(">");

}

void ClientClose(object sender, NetEventArgs e)
{
string info ;

if( e.Client.TypeOfExit == Session.ExitType.ExceptionExit)
{
info= string.Format("A Client Session:{0} Exception Closed.",
e.Client.ID);
}
else
{
info= string.Format("A Client Session:{0} Normal Closed.",
e.Client.ID);
}

Console.WriteLine( info );

Console.Write(">");
}

void RecvData(object sender, NetEventArgs e)
{
string info = string.Format("recv data:{0} from:{1}.",e.Client.Datagram, e.Client);

Console.WriteLine( info );
TcpSvr svr = (TcpSvr) sender;

//测试把收到的数据返回给客户端
svr.Send(e.Client, e.Client.Datagram);

Console.Write(">");

}

}
}

B.说明:
使用命令来操作服务器
exit 退出
start 开始服务
kick 关闭客户端
send 发送数据
list 列出所有客户端的状态
count 客户端计数

先启动服务运行start,等待客户端连接。
然后可以使用list,count察看当前的连接状况

每个事件都有相应函数,客户就在事件处理函数中处理自己的业务逻辑
可以通过继承特化自己的服务器应用,基本的框架不变

二.TcpCli的使用方法
A.测试程序:
using System;
using Ibms.Net.TcpCSFramework;

namespace Ibms.Test
{
/// <summary>
/// TestTcpClient 的摘要说明。
/// </summary>
public class TestTcpClient
{
public TestTcpClient()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
public static void Test()
{
Console.WriteLine("Begin to Test TcpCli Class..");

TestTcpClient test = new TestTcpClient();

TcpCli cli = new TcpCli( new Coder(Coder.EncodingMothord.UTF8));

cli.Resovlver = new DatagramResolver("##");

cli.ReceivedDatagram += new NetEvent(test.RecvData);

cli.DisConnectedServer += new NetEvent(test.ClientClose);

cli.ConnectedServer += new NetEvent(test.ClientConn);

try
{
//命令控制循环
while(true)
{
Console.Write(">");

string cmd=Console.ReadLine();

if(cmd.ToLower() == "exit")
{
break;
}

if(cmd.ToLower() == "close")
{
cli.Close();

continue;
}

if(cmd.ToLower().IndexOf("conn")!=-1)
{
cmd = cmd.ToLower();

string[] para = cmd.Split(' ');

if(para.Length ==3)
{

cli.Connect(para[1],int.Parse(para[2]));
}
else
{
Console.WriteLine("Error Command");
}

continue;
}

if(cmd.ToLower().IndexOf("send") !=-1)
{

cmd = cmd.ToLower();

string[] para = cmd.Split(' ');

if(para.Length ==2)
{

cli.Send(para[1]);

}
else
{
Console.WriteLine("Error Command");
}

continue;
}

Console.WriteLine("Unkown Command");

}//end of while

Console.WriteLine("End service");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}

}

void ClientConn(object sender, NetEventArgs e)
{
string info = string.Format("A Client:{0} connect server :{1}",e.Client,
e.Client.ClientSocket.RemoteEndPoint.ToString());

Console.WriteLine( info );

Console.Write(">");
}

void ClientClose(object sender, NetEventArgs e)
{
string info ;

if( e.Client.TypeOfExit == Session.ExitType.ExceptionExit)
{
info= string.Format("A Client Session:{0} Exception Closed.",
e.Client.ID);
}
else
{
info= string.Format("A Client Session:{0} Normal Closed.",
e.Client.ID);
}

Console.WriteLine( info );

Console.Write(">");
}

void RecvData(object sender, NetEventArgs e)
{
string info = string.Format("recv data:{0} from:{1}.",e.Client.Datagram, e.Client);

Console.WriteLine( info );

Console.Write(">");

}
}
}

B.说明:
先建立连接,Conn 192.9.207.214 9050
然后可以Send 数据
最后关闭连接Close

三.编码器
如果你要加密你的报文,需要一个你自己的Coder
从Coder类继承一个如MyCoder类,然后重载编码和解码函数。
使用方法:

TcpCli cli = new TcpCli( new MyCoder());

就可以在客户端使用该编码器了。
四.报文解析器
与编码器同样的实现方法。
c#(Socket)异步套接字代码示例
下面的示例程序创建一个连接到服务器的客户端。该客户端是用异步套接字生成的,因此在等待服务器返回响应时不挂起客户端应用程序的执行。该应用程序将字符串发送到服务器,然后在控制台显示该服务器返回的字符串。
C#
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
// State object for receiving data from remote device.
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient {
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient() {
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect( remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client,"This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client) {
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback( IAsyncResult ar ) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args) {
StartClient();
return 0;
}
}
异步服务器套接字示例 下面的示例程序创建一个接收来自客户端的连接请求的服务器。该服务器是用异步套接字生成的,
因此在等待来自客户端的连接时不挂起服务器应用程序的执行。该应用程序接收来自客户端的字符串,
在控制台显示该字符串,然后将该字符串回显到客户端。来自客户端的字符串必须包含字符串“<EOF>”,
以发出表示消息结尾的信号。

C#
复制代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
// State object for reading client data asynchronously
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener {
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener() {
}
public static void StartListening() {
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Bind the socket to the local endpoint and listen for incoming connections.
try {
listener.Bind(localEndPoint);
listener.Listen(100);
while (true) {
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener );
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Console.WriteLine("/nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar) {
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar) {
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer,0,bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1) {
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. /n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, content);
} else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private static void Send(Socket handler, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket handler = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args) {
StartListening();
return 0;
}
}

只要是异步,肯定是多线程的,你是什么意思啊?
用BeginReceive开始接收到一个信息,接收完后ReceiveCallback,在此期间接收的其他数据都需要新开一个线程进行接收,怎么能只有一个线程呢?

[C#]
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

// State object for reading client data asynchronously
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}

public class AsynchronousSocketListener {

// Incoming data from client.
public static string data = null;

// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);

public AsynchronousSocketListener() {
}

public static void StartListening() {
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];

// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );

// Bind the socket to the local endpoint and listen for incoming connections.
try {
listener.Bind(localEndPoint);
listener.Listen(100);

while (true) {
// Set the event to nonsignaled state.
allDone.Reset();

// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener );

// Wait until a connection is made before continuing.
allDone.WaitOne();
}

} catch (Exception e) {
Console.WriteLine(e.ToString());
}

Console.WriteLine("/nPress ENTER to continue...");
Console.Read();

}

public static void AcceptCallback(IAsyncResult ar) {
// Signal the main thread to continue.
allDone.Set();

// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);

// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}

public static void ReadCallback(IAsyncResult ar) {
String content = String.Empty;

// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;

// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer,0,bytesRead));

// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1) {
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. /n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, content);
} else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}

private static void Send(Socket handler, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);

// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}

private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket handler = (Socket) ar.AsyncState;

// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);

handler.Shutdown(SocketShutdown.Both);
handler.Close();

} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}

public static int Main(String[] args) {
StartListening();
return 0;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: