近期做了个TCP/IP异步通讯服务端实现方法,也是在网上胡乱搜索,然找了个自认为比较好的,然后封装一下,供后面自个使用,也供大家参考,如有不好的地方,欢迎指正,谢谢!
下面说一下这个方法里面的几个知识点:
1、托管
这个东西真心好用,虽然不知道具体怎么弄的,托管可以实现一个对象中的方法交由其他对象实现,而且可以同时触发多个方法,组件的触发函数就是由托管实现的,具体实现如下:
先声明一个托管的方法类型
public delegate void RecieveMsg(string IP_addr, int port, byte[] bytes, int length);
然后使用该托管类型声明一个方法
public event RecieveMsg OnRecieve;
最后直接在其他方法中调用该方法就可以了,如下
if (bytesRead > 0)//接收到数据
{
OnRecieve(client.IP_addr, client.Port, client.rec_buffer, bytesRead);//数据处理,由外部调用提供函数
}
外部实现该方法时(注意里面有个小知识点,将string类型转换为char[] 类型方法:Encoding.UTF8.GetBytes())
socket_server.OnRecieve += new AsyncSocketServer.RecieveMsg(RecieveHandle);
private void RecieveHandle(string IP_addr, int port, byte[] bytes, int length)
{
string strFileName = @"d:/data.txt";
if (!File.Exists(strFileName))
{
File.CreateText(strFileName);
}
string temp;
FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
temp = "\r\n" + IP_addr + " " + port.ToString() + ":";
fs.Position = fs.Length;
fs.Write(Encoding.UTF8.GetBytes(temp), 0, temp.Length);
fs.Position = fs.Length;
fs.Write(bytes, 0, length);
fs.Close();
socket_server.Send(IP_addr, port, bytes, length);
}
2、刷新连接状态
使用异步通信不能实时获得各路client连接的状态,这里有个方法可以实现,当返回为真,则说明连接中断,该部分放在一个定时函数中定时执行:
if (((socket.Poll(1000, SelectMode.SelectRead) && (socket.Available == 0)) || !socket.Connected)) //连接中断
{
}
3、设置系统定时函数,注意在windows服务中一般是不允许出现窗体的,所以在函数中对form类的一些控件建议不要使用,虽然编译不会出错,但控件对应的方法却不会执行。
先声明一个定时器:
System.Timers.Timer updata_time;
初始化定时器:
updata_time = new System.Timers.Timer(1000);//定时1s
updata_time.Enabled = true;//使能定时器
updata_time.AutoReset = true;//设置为循环执行
updata_time.Elapsed += new System.Timers.ElapsedEventHandler(this.updata_time_Tick);
最后实现定时调用的函数 updata_time_Tick。
4、重载,本次使用了一个单独的类来封装客户端,为了便于集成管理,需要对一些方法重载,如Equals,特别是对ArrayList类,所有涉及到查找的方法都涉及到Equals方法,其重载实现如下:
public override bool Equals(Object obj)
{
IP_Port IP_port = obj as IP_Port;//传入参数为 IP_Port
if (IP_port != null)
{
if ((IP_port.IP_addr == this.IP_addr) && (IP_port.port == this.Port))
{
return true;
}
else
{
return false;
}
}
Client client = obj as Client;//传入参数为Client
if (client != null)
{
if ((client.IP_addr == this.IP_addr) && (client.Port == this.Port))
{
return true;
}
else
{
return false;
}
}
return false;
}
注意里面 IP_Port IP_port = obj as IP_Port 可以判断传入的参数是什么类型,这样可以根据不同的传入参数做不同的方法,增加灵活性。
其他的方法是用如下:
clients.Remove(clients[i]);
int index = clients.IndexOf(ip_port);
这个类的使用方法很简单:
AsyncSocketServer socket_server;
socket_server.OnRecieve += new AsyncSocketServer.RecieveMsg(RecieveHandle);
socket_server.OnSend += new AsyncSocketServer.SendMsg(SendBackHandle);
socket_server.OnException += new AsyncSocketServer.ExceptionMsg(ExceptionHandle);
socket_server.StartListen();
还有在结束时调用StopListen()
具体源文件如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
namespace AsyncSocketServerApp
{
class AsyncSocketServer
{
#region 公共成员
public int port;//需要监听的本机端口号
public delegate void RecieveMsg(string IP_addr, int port, byte[] bytes, int length);//接受数据后,需要外部函数来处理
public event RecieveMsg OnRecieve;
public delegate void SendMsg(string IP_addr, int port, int length);//发送数据完成后,需要外部函数来处理
public event SendMsg OnSend;
public delegate void ExceptionMsg(string IP_addr, int port, Exception e);//发生异常函数
public event ExceptionMsg OnException;
#endregion
#region 私有成员
private Socket socket;
private ArrayList clients;
static private bool bListening = false;
Thread thread;
System.Timers.Timer updata_time;
#endregion
#region 公共方法
public AsyncSocketServer()
{
port = 1234;//默认端口号
SystemInit();
}
public AsyncSocketServer(int _port)
{
port = _port;//默认端口号
SystemInit();
}
~AsyncSocketServer()//析构函数
{
try
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
catch { }
}
public void StartListen()//开始监听
{
if (bListening == false)
{
thread = new Thread(new ThreadStart(this.SocketInit));
bListening = true;
thread.Start();
}
}
public void StopListen()//停止监听
{
if (bListening == true)
{
try
{
socket.Shutdown(SocketShutdown.Both);
}
catch { }
socket.Close();
thread.Abort();
bListening = false;
}
}
public int Send(string IP_addr, int port, byte[] bytes, int length)//发送数据到客户端
{
if (clients.Count == 0) return -1;
IP_Port ip_port = new IP_Port(IP_addr, port);
int index = clients.IndexOf(ip_port);
if (index < 0) return -1;
Client client = (Client)clients[index];
client.socket.BeginSend(bytes, 0, length, 0, new AsyncCallback(SendCallback), client);
return length;
}
public ArrayList GetClientList()//获取客户端列表
{
ArrayList client_list = new ArrayList();
IP_Port ip_port;
Monitor.Enter(clients);//获取对象锁防止在访问clients时updata_time_Tick线程修改 add by LC 2014.08.18 11:14
try
{
if (clients.Count == 0) return null;
foreach (Object obj in clients)
{
Client client = (Client)obj;
ip_port = new IP_Port(client.IP_addr, client.Port);
client_list.Add(ip_port);
}
}
finally { Monitor.Exit(clients); }//释放该对象,必须执行 add by LC 2014.08.18 11:15
return client_list;
}
#endregion
private void SystemInit()//form类的定时器改成System.Timers.Timer类 modfiy by LC 2014.08.14 16:51
{
clients = new ArrayList();
updata_time = new System.Timers.Timer(1000);//定时1s
updata_time.Enabled = true;//使能定时器
updata_time.AutoReset = true;//设置为循环执行
updata_time.Elapsed += new System.Timers.ElapsedEventHandler(this.updata_time_Tick);
}
private void SocketInit()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ip_end_point = new IPEndPoint(IPAddress.Any, port);
socket.Bind(ip_end_point);
socket.Listen(int.MaxValue);//开始监听,可连接数量设为最大
socket.BeginAccept(new AsyncCallback(AcceptCallback), socket);
}
private void AcceptCallback(IAsyncResult ar)
{
try
{
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
EndPoint ip = handler.RemoteEndPoint;
Client client = new Client();
client.socket = handler;
//IP_Port ip_port = new IP_Port((handler.RemoteEndPoint as IPEndPoint).Address.ToString() , (handler.RemoteEndPoint as IPEndPoint).Port);
if (!clients.Contains(client))//判断原来有没有连接,若没有,则添加到客户端列表
{
clients.Add(client);
}
handler.BeginReceive(client.rec_buffer, 0, Client.BufferSize, 0, new AsyncCallback(ReadCallback), client);
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);//继续侦听其他连接
}
catch { }
}
private void ReadCallback(IAsyncResult ar)
{
Client client = (Client)ar.AsyncState;
try
{
Socket handler = client.socket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)//接收到数据
{
OnRecieve(client.IP_addr, client.Port, client.rec_buffer, bytesRead);//数据处理,由外部调用提供函数
}
handler.BeginReceive(client.rec_buffer, 0, Client.BufferSize, 0, new AsyncCallback(ReadCallback), client);
}
catch(Exception re)
{ OnException(client.IP_addr, client.Port, re); }
}
private void SendCallback(IAsyncResult ar)//发送成功后的回调函数
{
Client client = (Client)ar.AsyncState;
try
{
int bytes_sent = client.socket.EndSend(ar);
OnSend(client.IP_addr, client.Port, bytes_sent);//发送成功后返回
}
catch (Exception re)
{ OnException(client.IP_addr, client.Port, re); }
}
private void updata_time_Tick(object sender, EventArgs e)//1s执行一次
{
if (clients.Count == 0) return;
Monitor.Enter(clients);// add by LC 2014.08.18 11:17
try
{
for (int i = 0; i < clients.Count; i++)
{
Socket socket = ((Client)clients[i]).socket;//指向错误 modfiy by LC 2014.08.14 16:58
if (((socket.Poll(1000, SelectMode.SelectRead) && (socket.Available == 0)) || !socket.Connected)) //连接中断
{
//socket.Shutdown(SocketShutdown.Both);
//socket.Close();
//MessageBox.Show((socket.RemoteEndPoint as IPEndPoint).Address.ToString() + ":" + (socket.RemoteEndPoint as IPEndPoint).Port.ToString() + "连接中断");
clients.Remove(clients[i]);
}
}
}
catch (Exception re)
{ OnException("updata list ", 0, re); }
Monitor.Exit(clients);// add by LC 2014.08.18 11:17
}
}
public class IP_Port
{
public string IP_addr;
public int port;
public IP_Port(string e_IP, int e_port)
{
IP_addr = e_IP;
port = e_port;
}
}
public class Client
{
private int _port;//端口号
private string _IP_addr;//IP号
public Socket socket;
public static int BufferSize = 1024; // Receive buffer.
public byte[] rec_buffer = new byte[BufferSize]; // Received data string.
~Client()
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
public int Port//读取端口号
{
get
{
_port = (socket.RemoteEndPoint as IPEndPoint).Port;
return _port;
}
}
public string IP_addr
{
get
{
_IP_addr = (socket.RemoteEndPoint as IPEndPoint).Address.ToString();
return _IP_addr;
}
}
public override bool Equals(Object obj)
{
IP_Port IP_port = obj as IP_Port;//传入参数为 IP_Port
if (IP_port != null)
{
if ((IP_port.IP_addr == this.IP_addr) && (IP_port.port == this.Port))
{
return true;
}
else
{
return false;
}
}
Client client = obj as Client;//传入参数为Client
if (client != null)
{
if ((client.IP_addr == this.IP_addr) && (client.Port == this.Port))
{
return true;
}
else
{
return false;
}
}
return false;
}
public override int GetHashCode()
{
return Port;
}
public void RelaseTcpClient()//释放TcpClient资源
{
try
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket.Close();
}
catch
{
}
}
}
}