版权声明:未经同意,请勿转载 https://blog.csdn.net/qq_25542475/article/details/86790203
本章主要讲述服务端通过创建连接类,实现多个客户端的连接管理。
一 为什么创建连接类
服务端要处理多个客户端消息,它需要用一个数组来维护所有客户端的连接。每个客户端都有自己的Socket以及缓冲区。
二 业务逻辑的实现
(1)创建Conn连接类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using TCP_Sever.Timer;
namespace TCP_Sever.Conns
{
class Conn
{
//常量缓存池大小
public const int Buff_Size = 1024;
//客户端连接socket
public Socket Client_Socket;
//使用状态
public bool isUse = false;
//缓存池
public byte[] ReadBuff;
//数据大小
public int count = 0;
//构造函数
public Conn()
{
ReadBuff = new byte[Buff_Size];
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="socket"></param>
public void Init(Socket socket)
{
Client_Socket = socket;
isUse = true;
count = 0;
}
/// <summary>
/// 缓冲区剩余字节数
/// </summary>
/// <returns></returns>
public int Buff_Remain()
{
return Buff_Size - count;
}
/// <summary>
/// 获取客户端地址
/// </summary>
/// <returns></returns>
public string Get_ClientAddress()
{
if (!isUse)
{
return "无法获取客户端地址";
}
return Client_Socket.RemoteEndPoint.ToString();
}
/// <summary>
/// 关闭连接
/// </summary>
public void Client_Close()
{
if (!isUse)
return;
Client_Socket.Close();
isUse = false;
Console.WriteLine(Get_ClientAddress() + "断开连接");
}
}
}
(2)创建Conn_Helper类管理Conn的使用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TCP_Sever.Conns
{
class Conn_Helper
{
/// <summary>
/// 连接池列表
/// </summary>
public static Conn[] Conns;
/// <summary>
/// 初始化
/// </summary>
/// <param name="coun_size"></param>
public static void Init(int conns_size)
{
if (Conns == null)
{
Conns = new Conn[conns_size];
}
for (int i = 0; i < conns_size; i++)
{
Conns[i] = new Conn();
}
}
/// <summary>
/// 获取可以使用的连接
/// </summary>
/// <returns></returns>
public static Conn Get_NewConn()
{
if (Conns == null)
return null;
for (int i = 0; i < Conns.Length; i++)
{
if (Conns[i] == null)
{
Conns[i] = new Conn();
return Conns[i];
}
else if (!Conns[i].isUse)
{
return Conns[i];
}
}
return null;
}
}
}
(3)修改Sever类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using TCP_Sever.Conns;
using TCP_Sever.Timer;
namespace TCP_Sever.Tcpsever
{
class Sever
{
/// <summary>
/// 服务器套接字
/// </summary>
private static Socket sever_socket;
/// <summary>
/// 最大连接数
/// </summary>
private static int maxConn = 50;
/// <summary>
/// 数据缓存池
/// </summary>
private static byte[] readBuff = new byte[1024];
/// <summary>
/// 开启服务器
/// </summary>
/// <param name="host">IP地址</param>
/// <param name="port">端口号</param>
public static void Start(string IP, int port)
{
//创建服务器套接字
sever_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定IP和端口号
IPAddress ip = IPAddress.Parse(IP);
IPEndPoint point = new IPEndPoint(ip, port);
sever_socket.Bind(point);
//监听,0无限制
sever_socket.Listen(maxConn);
Conn_Helper.Init(maxConn);
HeartbeatTime.Start();
Console.WriteLine("服务器开启成功");
sever_socket.BeginAccept(AcceptCallBack, null);
}
/// <summary>
/// 同步接收与发送
/// </summary>
public static void SynRecv_Send()
{
//客户端接入
if (sever_socket == null)
{
Console.WriteLine("请先开启服务器");
return;
}
Socket connfd = sever_socket.Accept();
Console.WriteLine("客户端接入");
//获取接收到的流转换为字符串
int count;
try
{
count= connfd.Receive(readBuff);
}
catch (Exception e)
{
Console.WriteLine("客户端异常"+e.Message);
return;
}
string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
Console.WriteLine("收到信息:" + str);
//获取输入,发送给客户端
str="服务器接收到消息:"+str;
byte[] writeBuff = System.Text.Encoding.UTF8.GetBytes(str);
connfd.Send(writeBuff);
connfd.Close();
}
/// <summary>
/// Accept回调
/// </summary>
/// <param name="ar"></param>
private static void AcceptCallBack(IAsyncResult ar)
{
try
{
if (Conn_Helper.Get_NewConn() == null)
{
Console.WriteLine("连接已满");
}
else
{
Conn conn = Conn_Helper.Get_NewConn();
conn.Init(sever_socket.EndAccept(ar));
Console.WriteLine("客户端连接" + conn.Get_ClientAddress());
AsynRec(conn);
}
sever_socket.BeginAccept(AcceptCallBack, null);
}
catch (Exception e)
{
Console.WriteLine("AcceptCb失败:" + e.Message);
}
}
/// <summary>
/// 异步接收
/// </summary>
public static void AsynRec(Conn conn)
{
//客户端接入
if (sever_socket == null)
{
Console.WriteLine("请先开启服务器");
return;
}
Console.WriteLine("客户端接入");
//异步接收
try
{
conn.Client_Socket.BeginReceive(conn.ReadBuff, conn.count, conn.Buff_Remain(), SocketFlags.None,AsynReceiveCallBack, conn);
}
catch (Exception e)
{
Console.WriteLine("客户端异常"+e.Message);
return;
}
}
/// <summary>
/// 异步接收回调
/// </summary>
/// <param name="ar"></param>
private static void AsynReceiveCallBack(IAsyncResult ar)
{
Conn conn = (Conn)ar.AsyncState;
lock (conn)
{
try
{
conn.count= conn.Client_Socket.EndReceive(ar);
string str = System.Text.Encoding.UTF8.GetString(conn.ReadBuff, 0, conn.count);
if (str == "HeartTime")
{
HeartbeatTime.Update_last_HeatTime(conn);
}
Console.WriteLine("接收到消息:" + str);
for (int i = 0; i < Conn_Helper.Conns.Length; i++)
{
if (!Conn_Helper.Conns[i].isUse)continue;
AsynSend(Conn_Helper.Conns[i]);
}
//继续接收,实现循环
conn.Client_Socket.BeginReceive(readBuff,conn.count ,conn.Buff_Remain(), SocketFlags.None, AsynReceiveCallBack, conn);
}
catch (Exception e)
{
Console.WriteLine("客户端断开:" + e.Message);
}
}
}
/// <summary>
/// 异步发送
/// </summary>
public static void AsynSend(Conn conn)
{
//客户端接入
if (sever_socket == null)
{
Console.WriteLine("请先开启服务器");
return;
}
byte[] writerBuff = System.Text.Encoding.UTF8.GetBytes("客户端你好"+conn.Get_ClientAddress());
try
{
//异步发送
conn.Client_Socket.BeginSend(writerBuff, 0, writerBuff.Length, SocketFlags.None, null, null);
}
catch (Exception e)
{
Console.WriteLine("发送消息失败" + e.Message);
}
}
}
}
(4)修改Program.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TCP_Sever.Tcpsever;
namespace TCP_Sever
{
class Program
{
static void Main(string[] args)
{
Sever.Start("127.0.0.1", 6688);
while (true)
{
//同步
//Sever.SynRecv_Send();
//异步
// Sever.AsynRec();
}
}
}
}