心跳机制服务器
Server
cs
TcpListener listen;
public Server(IPAddress ip,int port)
{
listen = new TcpListener(ip, port);
}
public void Start()
{
listen.Start(100);
StartConnect();
}
Dictionary<string,TcpClient> clientDic = new Dictionary<string,TcpClient>();
public event Action<TcpClient> 有客户端连入的事件;
void StartConnect()
{
Task.Run(() =>
{
while (true)
{
TcpClient client = listen.AcceptTcpClient();
string ip = client.Client.RemoteEndPoint.ToString();
clientDic.Add(ip, client);
有客户端连入的事件?.Invoke(client);
startRead(client);//读取粘包的数据,在这个方法进行拆包处理
}
});
}
//拆包
public void startRead(TcpClient t1)
{
//接受客户端发来的数据
NetworkStream stream = t1.GetStream();
string ip = t1.Client.RemoteEndPoint.ToString();
byte[] bs = new byte[1024 * 1024];
Task.Run(() =>
{
try
{
while (true)
{
int count= stream.Read(bs, 0, bs.Length);
if (count== 0)
{
throw new Exception("客户端断了");
}
//接受数据
byte[] body = bs.Take(count).ToArray();
string s = Encoding.UTF8.GetString(body);
Console.WriteLine("-------------------------------");
Console.WriteLine("接收到消息为:"+s);
Console.WriteLine("-------------------------------");
Send(s);// 群发
}
}
catch (Exception ex)
{
clientDic.Remove(ip);
}
});
}
public event Action<string> 客户端断开事件;
public event Action<TcpClient, byte[]> 接受到消息的事件;
public Server()
{
}
//群发方法 向所有的客户端发消息
public void Send(string content)
{
byte[] bs = Encoding.UTF8.GetBytes(content);
foreach (var item in clientDic) //遍历所有的客户端
{
item.Value.GetStream().Write(bs, 0, bs.Length);
}
}
//指定给谁发
public void Send(string content,string ip) {
byte[] bs = Encoding.UTF8.GetBytes(content);
//根据ip取出客户端,从字典取
clientDic[ip].GetStream().Write(bs, 0, bs.Length);
}
//指定给哪些客户端发
//send("你好", ["192.","127"])
public void Send(string content, string[] ips)
{
byte[] bs = Encoding.UTF8.GetBytes(content);
foreach (var item in clientDic) //所有客户端
{
//item.key 键 ip字符串
//item.value 值 客户端对象
if (ips.Contains(item.Key))
{
//如果ips数组包含目标客户端
item.Value.GetStream().Write(bs, 0, bs.Length);
}
}
}
Program
cs
static Server s;
static void Main(string[] args)
{
s = new Server(IPAddress.Any,8080);
s.有客户端连入的事件 += f1;
s.接受到消息的事件 += f2;
s.Start();
Console.ReadKey();
}
public static void f1(TcpClient t1)
{
Console.WriteLine(t1.Client.RemoteEndPoint.ToString()+"连接到服务器");
}
public static void f2(TcpClient t2, byte[] bs)
{
Console.WriteLine(t2.Client.RemoteEndPoint.ToString()+"发来的消息为+++++++++++:"+ Encoding.UTF8.GetString(bs,0,bs.Length));
}
客户端
cs
TcpClient client;
public Form1()
{
InitializeComponent();
client = new TcpClient();
client.Connect("192.168.107.60",8080);
// 开始接收数据
startReceive(client);
}
void startReceive(TcpClient t1)
{
byte[] bs = new byte[1024]; // 缓存区
Task.Run(() =>
{
try
{
while (true)
{
int count = t1.GetStream().Read(bs, 0, bs.Length);
if(count == 0)
{
Console.WriteLine("服务器断开");
break;
}
// 去掉数据最后一位占位0
byte[] data = bs.Take(count).ToArray();
// 需不需要拆包
Console.WriteLine(Encoding.UTF8.GetString(data));
if(lastPack != null)
{
data = lastPack.Concat(data).ToArray();
lastPack = null;
}
ChaiBao(data, 0, t1);
}
}
catch (Exception ex)
{
Console.WriteLine("客户端断开");
}
});
}
byte[] lastPack = null;// 上一个半包
public void ChaiBao(byte[] bs,int startIndex,TcpClient t1)
{
if (startIndex + 4 > bs.Length)
{
lastPack = bs.Skip(startIndex).ToArray(); // 跳过开始位置取出后面的
return;
}
// 记录第一个包的长度
int len = BitConverter.ToInt32(bs, startIndex);
// 取出之前所有包的长度
int abc = len + startIndex + 4;
Console.WriteLine(abc + "?????" + bs.Length);
if(abc == bs.Length)
{
byte[] bs1 = bs.Skip(startIndex + 4).ToArray();
richTextBox1.Invoke((Action)(() =>
{
richTextBox1.AppendText(Encoding.UTF8.GetString(bs1) + "\n");
}));
}else if (abc < bs.Length)
{
byte[] bs2 = bs.Skip(startIndex + 4).Take(len).ToArray();
richTextBox1.Invoke((Action)(() =>
{
richTextBox1.AppendText(Encoding.UTF8.GetString(bs2) + "\n");
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.ScrollToCaret();
}));
// 继续拆包
ChaiBao(bs, abc, t1);
}
else
{
lastPack = bs.Skip(startIndex).ToArray();
}
}
直接发消息
调用MemoryStream进行添加数据长度的方法
cs
Send(client.GetStream(), "大帅的几种形态");
Send(client.GetStream(), "精致大帅");
Send(client.GetStream(), "油腻大帅");
Send(client.GetStream(), "虚币大帅");
Send(client.GetStream(), "变态大帅");
Send(client.GetStream(), "人妻大帅");
没有使用MemoryStream进行添加数据长度
cs
Send("罗罗诺亚·索隆");
Send("蒙奇·D·路飞");
Send("文斯莫克·山治");
Send("托尼托尼·乔巴");
Send("妮可·罗宾");
MemoeySteream内存流把数据长度和数据内容写在一个包里
cs
void Send(NetworkStream stream, string msg)
{
byte[] bs = Encoding.UTF8.GetBytes(msg); // 先转成字符节数组
MemoryStream ms = new MemoryStream();// 创建内存流
BinaryWriter bw = new BinaryWriter(ms); // 创建内存流
bw.Write(bs.Length);// 写入长度
bw.Write(bs);// 写入内容
stream.Write(ms.ToArray(), 0, ms.ToArray().Length);// 发送内容流
bw.Close();// 关闭写入对象
ms.Close();// 关闭内存流
}
void Send(string msg)
{
byte[] bs = Encoding.UTF8.GetBytes(msg); // 把发的消息转成字节数组
// BitConverter 转成位数据
// 把字节数组长度转成位格式数据
byte[] len = BitConverter.GetBytes(bs.Length);
byte[] body = len.Concat(bs).ToArray(); // 把数据长度和内容存在一个包里面
client.GetStream().Write(body, 0, body.Length);
}