【 tulaoshi.com - 编程语言 】
tcp一般用于维持一个可信任的连接,比起udp更为安全可靠,在vs.net,分别有tcpclient和udpclient以及tcplistener,一般开发中基本可以满足需要,但是这个有个很大的弊端,对于维持一个时间较长的,相互交互的来说,数据处理不是很明朗,vs/net中还有一个socket类,用他来做一个客户/服务器段,同时在接发数据的时候,能相互独立,这需要一个异步通讯过程
先实现服务器段:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
// State object for reading client data asynchronously
namespace TcpServer
{
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);
private static Socket listener;
private int _port=9010;
public AsynchronousSocketListener()
{
}
public void StopListening()
{
listener.Close();
}
public int Port
{
set
{
_port=value;
}
}
public 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, _port);
// Create a TCP/IP 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("接收连接..");
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();
}
private 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);
}
private 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;
int bytesRead=0 ;
// Read data from the client socket.
if(handler.Connected )
{
try
{
bytesRead = handler.EndReceive(ar);
}
catch(Exception ex)
{
handler.Close();
}
if (bytesRead 0)
{
// There might be more data, so store the data received so far.
// Check for end-of-file tag. If it is not there, read
// more data.
content = Encoding.ASCII.GetString(
state.buffer,0,bytesRead);
if (content.Length0 && content.EndsWith("EOF") )
{
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("从客户端收到 {0} bytes 数据. n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, "-数据确认,已经收到-EOF");
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
}
private void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.UTF8.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private 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("发送 {0} bytes 到客户端.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
具体调用如下:
string p="";
AsynchronousSocketListener _server=new AsynchronousSocketListener();
_server.StartListening();
if((p=Console.ReadLine().ToLower() )!="exit" )
{
_server.StopListening();
}
紧接着实现客户端,客户端稍微复杂点,用一个session类来维持一个会话过程,coder类实现多种编码,Datagram类定义一个具体的数据报文,默认为64个字节大小,
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Data;
using System.Xml;
using System.Xml.XPath;
namespace Client
{
#region 通讯对象
public delegate void NetEvent(object sender, NetEventArgs e);
public class CSocket
{
#region 字段
/// summary
/// 客户端与服务器之间的会话类
/// /summary
private Session _session;
/// summary
/// 客户端是否已经连接服务器
/// /summary
private bool _isConnected = false;
private bool _isEcho = false;
private StringBuilder sb=new StringBuilder();
/// 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];
public ManualResetEvent allDone = new ManualResetEvent(false);
#endregion
#region 事件定义
//需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅
/// summary
/// 已经连接服务器事件
/// /summary
/// summary
/// 接收到数据报文事件
/// /summary
public event NetEvent ReceivedDatagram;
public event NetEvent DisConnectedServer;
public event NetEvent ConnectedServer;
/// summary
/// 连接断开事件
/// /summary
#endregion
#region 属性
/// summary
/// 返回客户端与服务器之间的会话对象
/// /summary
public Session ClientSession
{
get
{
return _session;
}
}
/// summary
/// 返回客户端与服务器之间的连接状态
/// /summary
public bool IsConnected
{
get
{
return _isConnected;
}
}
public bool IsEchoBack
{
get
{
return _isEcho;
}
}
/// summary
/// 数据报文分析器
/// /summary
public DatagramResolver Resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}
/// summary
/// 编码解码器
/// /summary
public Coder ServerCoder
{
get
{
return _coder;
}
}
#endregion
#region 公有方法
/// summary
/// 默认构造函数,使用默认的编码格式
/// /summary
public CSocket()
{
_coder = new Coder( Coder.EncodingMothord.gb2312 );
}
/// summary
/// 构造函数,使用一个特定的编码器来初始化
/// /summary
/// param name="_coder"报文编码器/param
public CSocket( 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)
{
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)
{
try
{
if(datagram.Length ==0 )
{
return;
}
allDone.WaitOne();
//获得报文的编码字节
byte [] data = _coder.GetEncodingBytes(datagram);
_session.ClientSocket.BeginSend( data, 0, data.Length, SocketFlags.None,
new AsyncCallback( SendDataEnd ), _session.ClientSocket);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString() );
}
}
/// 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)
{
try
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString() );
}
}
/// 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;
allDone.Set();
try
{
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0,
DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), socket);}
catch(Exception ex)
{
socket.Close();
}
}
/// summary
/// 数据接收处理函数
/// /summary
/// param name="iar"异步Socket/param
public string recevie()
{
return this.sb.ToString() ;
}
protected virtual void RecvData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
try
{
string receivedData="" ;
int recv = remote.EndReceive(iar);
if(recv0)
{
receivedData = System.Text.Encoding.UTF8.GetString(_recvDataBuffer,0,recv ) ;
if(receivedData.EndsWith("EOF") )
{
_isEcho=true;
sb.Append(receivedData);
this._session.Datagram= receivedData;
if(ReceivedDatagram==null)
{
ReceivedDatagram(this,new NetEventArgs(_session) ) ;
}
Console.WriteLine(string.Format("{0},来自{1}",receivedData,_session.ClientSocket.RemoteEndPoint.ToString() ) ) ;
this.allDone.Set();
}
else
{
Console.WriteLine("listen");
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), _session.ClientSocket);
}
}
}
catch(SocketException ex)
{
Console.WriteLine(ex.ToString() );
}
}
#endregion
}
/// summary
/// 通讯编码格式提供者,为通讯服务提供编码和解码服务
/// 你可以在继承类中定制自己的编码方式如:数据加密传输等
/// /summary
public class Coder
{
/// summary
/// 编码方式
/// /summary
private EncodingMothord _encodingMothord;
protected Coder()
{
}
public Coder(EncodingMothord encodingMothord)
{
_encodingMothord = encodingMothord;
}
public enum EncodingMothord
{
gb2312=0,
Default ,
Unicode,
UTF8,
ASCII,
}
/// summary
/// 通讯数据解码
/// /summary
/// param name="dataBytes"需要解码的数据/param
/// returns编码后的数据/returns
public virtual string GetEncodingString( byte [] dataBytes,int size)
{
switch( _encodingMothord )
{
case EncodingMothord.gb2312:
{
return Encoding.GetEncoding("gb2312").GetString(dataBytes,0,size);
}
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.gb2312:
{
return Encoding.GetEncoding("gb2312").GetBytes(datagram);
}
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
{
&nbs