| state | 表示状态信息,必须保证state中包含socket句柄 |
Socket EndAccept(IAsyncResult asyncResult)
写在回调函数中,用于获取客户端套接字
调用该函数后,程序继续执行,有客户端连接上,回调函数会执行。
IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state);
参数 | 说明 |
---|---|
buffer | Byte类型数组,用于存储接受到的数据 |
offset | buffer参数中存储数据的位置,该位置应该从零开始技术 |
size | 最多接受的字节数 |
socketFlags | SocketFlags值的按位组合,这里设置为0 |
callback | 回调函数,一个AsynCallback委托 |
state | 一个用户定义对象,其中包含接受操作的相关信息。递给EndReceiv委托 |
int EndReceive(IAsyncResult asyncResult);
写在BeginReceive函数中,用于得到接收数据的大小。
异步服务端如图:
服务端需要处理多个客户端消息,所以需要一个数组维持这些客户端的连接。每个客户端都有自己的名字,Socket和缓冲区。所以定义Conn类来描述客户端连接,然后用Conn数组维持所有客户端的连接。
using System;
using System.Net;
using System.Net.Sockets;
namespace Server
{
class Conn
{
public const int BUFFER_SIZE = 1024;
public Socket socket;
public bool isUse = false;//该客户端是否在使用中
public byte[] readBuff = new byte[BUFFER\_SIZE];//内容缓冲区
public int buffCount = 0;//当前缓冲区数据字节量
public string m_name = "";//客户端名字
public Conn()
{
readBuff = new byte[BUFFER\_SIZE];
}
public void Init(Socket socket)
{
this.socket = socket;
isUse = true;
buffCount = 0;
}
public int BuffRemain()//获取缓冲区剩余容量
{
return BUFFER_SIZE - buffCount;
}
public string GetAdress()//获取客户端套接字地址 ip:端口
{
if (!isUse)
return "无法获取地址";
return socket.RemoteEndPoint.ToString();
}
public void close()//关闭客户端
{
if(!isUse)
return;
Console.WriteLine("[断开连接]" + GetAdress());
socket.Close();
isUse = false;
}
}
class Serv
{
public Socket listenfd;//服务端套接字
public Conn[] conns;//连接池
public int maxConn = 50;//最大连接数
public int NewIndex()//从连接池获取一个没在使用的Conn的下标
{
if (conns == null)
return -1;
for (int i = 0; i < conns.Length; i++)
{
if (conns[i] == null)
{
conns[i] = new Conn();
return i;
}
else if (conns[i].isUse == false)
{
return i;
}
}
return -1;
}
public void Start(string host, int port)//初始化连接池,开始监听端口
{
conns = new Conn[maxConn];
for (int i = 0; i < maxConn; i++)
{
conns[i] = new Conn();
}
listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAdr = IPAddress.Parse(host);
IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
listenfd.Bind(ipEp);
listenfd.Listen(maxConn);
listenfd.BeginAccept(AcceptCb, null);
Console.WriteLine("[服务器]启动成功");
}
private void AcceptCb(IAsyncResult ar)//回调函数,接收到客户端的连接
{
try
{
Socket socket = listenfd.EndAccept(ar);
int index = NewIndex();
if (index < 0)
{
socket.Close();
Console.WriteLine("[警告]连接已满");
}
else
{
Conn conn = conns[index];
conn.Init(socket);
string adr = conn.GetAdress();
Console.WriteLine("客户端连接[" + adr + "] conn池ID:" + index);
conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);//接收客户端发送的内容
listenfd.BeginAccept(AcceptCb, null);//继续监听,等待客户端连接,
}
}
catch (System.Exception ex)
{
Console.WriteLine("AcceptCb失败:" + ex.Message);
}
}
private void ReceiveCb(IAsyncResult ar)//回调函数 接收到客户端内容进行调用
{
Conn conn = (Conn)ar.AsyncState;
try
{
int count = conn.socket.EndReceive(ar);//获取客户端内容的字节数
if (count <= 0)
{
Console.WriteLine("收到 【" + conn.m_name + "】断开连接 ");
conn.close();
return;
}
string str = System.Text.Encoding.Default.GetString(conn.readBuff, 0, count);
//将客户端名字和消息内容分割开 yar:hello =》yar hello
string[] arr = str.Split(':');
string name = "";
int t = 0;
foreach (string s in arr)
{
if (t == 0) name = s ;
t = 1;
str = s;
}
conn.m_name = name;
Console.WriteLine("收到 【" + conn.m_name + "】数据: " + str);
byte[] bytes = System.Text.Encoding.Default.GetBytes(conn.m_name+":"+str);
for (int i = 0; i < conns.Length; i++)//转播给其他正在使用的客户端
{
if (conns[i] == null)
continue;
if (!conns[i].isUse)
continue;
Console.WriteLine("将消息转播给" + conns[i].m_name);
conns[i].socket.Send(bytes);
}
conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);//继续接收该客户端的数据
}
catch (System.Exception ex)
{
Console.WriteLine("收到 【" + conn.m_name + "】断开连接 ");
conn.close();
return;
}
}
}
class Program
{
static void Main(string[] args)
{
Serv serv = new Serv();
serv.Start("127.0.0.1", 1234);
while (true)
{
string str = Console.ReadLine();
switch (str)
{
case "quit":
return;
}
}
}
}
}
2 客户端
客户端示意图:
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
namespace Client
{
class Cli
{
string m_name = "无名";
Socket socket;
const int BUFFER_SIZE = 1024;
public byte[] readBuff = new byte[BUFFER\_SIZE];
public Cli(String name)
{
m_name = name;
}
public void Connection()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
string host = "127.0.0.1";
int port = 1234;
socket.Connect(host, port);
// socket.LocalEndPoint.ToString();
socket.BeginReceive(readBuff,0,BUFFER_SIZE,SocketFlags.None,ReceiveCb,null);
}
private void ReceiveCb(IAsyncResult ar)
{
try
{
int count = socket.EndReceive(ar);
string str = System.Text.Encoding.Default.GetString(readBuff, 0, count);
Console.WriteLine(str);
socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null);
}
catch (System.Exception ex)
{
socket.Close();
}
}
public void Send(string str)
{
byte[] bytes = System.Text.Encoding.Default.GetBytes(m_name+":"+str);
try
{
socket.Send(bytes);
}
catch { }