此程序分服务端以及客户端,服务端负责信息的中转,最终实现微信的效果,服务器可挂在公务,并做了心跳检测。
客户端只需要能上网既能实现点对点通讯
采用TCP UDP混合异步编程
解决TCP粘包问题 以及UDP分包发送问题
可用于二次开发,值得深入学习
服务端代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using vjsdn.net.library;
using System.Windows.Forms;
using Common;
using Newtonsoft.Json;
using LitJson;
using System.Linq;
using System.IO;
using System.Diagnostics;
namespace vjsdn.net.library
{
///
///
/// 服务器端业务类
///
///
public class Server
{
private UdpClient _server; //服务器端消息监听器
private Socket serverListener; //客户监听器
public UserCollection _userList; //在线用户列表
public TCPUserCollection _TCPuserList; //在线用户列表
private Thread _serverThread;
private IPEndPoint _remotePoint; //远程用户请求的IP地址及端口
private Dictionary<string, string> userCollection;
private WriteLogHandle _WriteLogHandle = null;
private UserChangedHandle _UserChangedHandle = null;
////心跳包发送给客户端后,如果客户端有应答,那么下面这个集合中的value将一直保持为0的状态,否则就累加,一旦累加大于等于3次,则视为掉线。
public Dictionary<string, int> userOnLineCounter;
private AutoResetEvent syncHeartbeatSend;
private AutoResetEvent resetEvent;
private AutoResetEvent resetEventForListener;
[System.Runtime.InteropServices.DllImport(“psapi.dll”)]
private static extern int EmptyWorkingSet(int hProcess);
///
/// 显示跟踪消息
///
public WriteLogHandle OnWriteLog
{
get { return _WriteLogHandle; }
set { _WriteLogHandle = value; }
}
/// <summary> /// 当用户登入/登出时触发此事件 /// </summary> public UserChangedHandle OnUserChanged { get { return _UserChangedHandle; } set { _UserChangedHandle = value; } } /// <summary> /// 构造器 /// </summary> public Server() { _userList = new UserCollection(); _TCPuserList = new TCPUserCollection(); _remotePoint = new IPEndPoint(IPAddress.Any, 0); _serverThread = new Thread(new ThreadStart(Run)); resetEvent = new AutoResetEvent(false); resetEventForListener = new AutoResetEvent(false); serverListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } /// <summary> ///显示跟踪记录 /// </summary> /// <param name="log"></param> private void DoWriteLog(string log) { if (_WriteLogHandle != null) (_WriteLogHandle.Target as System.Windows.Forms.Control).Invoke(_WriteLogHandle, log); } /// <summary> /// 刷新用户列表 /// </summary> /// <param name="list">用户列表</param> private void DoUserChanged(UserCollection list) { if (_UserChangedHandle != null) (_UserChangedHandle.Target as Control).Invoke(_UserChangedHandle, list); } #region --开启服务-- public void Start() { try { userCollection = new Dictionary<string, string>(); userOnLineCounter = new Dictionary<string, int>(); //心跳 syncHeartbeatSend = new AutoResetEvent(false);//心跳 _server = new UdpClient(5000); _serverThread.Start(); //serverListener = new TcpListener(5001); IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, 5001); serverListener.Bind(ipEnd); serverListener.Listen(100); serverListener.NoDelay = false; serverListener.ReceiveTimeout = 1000000; serverListener.BeginAccept(new AsyncCallback(ClientConnectComplete), null); //serverListener.Start(); //AcceptClientsThread(); DoWriteLog("服务器已经启动,监听端口:" + Globals.SERVER_PORT.ToString() + ",等待客户连接..."); } catch (Exception ex) { DoWriteLog("启动服务器发生错误: " + ex.Message); throw ex; } } #endregion #region --新接收-- byte[] receiveByte = new byte[1024]; StringBuilder result=new StringBuilder(); private void ClientConnectComplete(IAsyncResult async) { TCPuser client; // 完成接受客户端传入的连接的这个异步操作,并返回客户端连入的 socket try { Socket sk = serverListener.EndAccept(async); client = new TCPuser(sk); } catch (Exception ex) { return; } //把连接进来的客户端保存起来 //ReceiveClientsThread((object)client); try { StateObject state = new StateObject(); state.workSocket = client.client; client.client.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ClientReceiveComplete), state); // 开始异步接收客户端传入的数据 serverListener.BeginAccept(new AsyncCallback(ClientConnectComplete), null);//继续接受下一个连接 } catch (SocketException ex) { // 处理异常 } } private void SendToClientComplete(IAsyncResult async) { TCPuser client = async.AsyncState as TCPuser; try { // 完成将信息发送到客户端的这个异步操作 if (client.client.Connected) { client.client.EndSend(async); } } catch (SocketException ex) { // 处理异常 } } string strresult; //#endregion #region --tcp 解析包-- private void TCPAnalysisPackage(MessageEntity entity, TCPuser user) { switch (entity.MessageType) { case MessagePicks.OnLine: user.name = entity.NickName; var disconnectedUsers = userOnLineCounter.Where(p => p.Key == entity.NickName).ToList(); if (disconnectedUsers.Count == 0) { userOnLineCounter.Add(entity.NickName, 0); } #region --udp-- IPEndPoint userEndPoint = new IPEndPoint(_remotePoint.Address, _remotePoint.Port); User user2 = new User(entity.NickName, userEndPoint); var udpuser = _userList.Find(entity.NickName); if (udpuser == null) { _userList.Add(user2); } #endregion var tcpuser = _TCPuserList.Find(entity.NickName); if (tcpuser == null) { _TCPuserList.Add(user); } else { //user.client.Close(); } this.DoUserChanged(_userList); BroadTCPCastUsers(); BroadCastUsers(); break; case MessagePicks.FileOnLine: byte[] recvBytes = entity.receiveClientVideoBytes; if (recvBytes[0] == 0x10 && recvBytes[1] == 0x13) { byte[] bytes2 = new byte[] { recvBytes[2], recvBytes[3], recvBytes[4], recvBytes[5], recvBytes[6], recvBytes[7], recvBytes[8], recvBytes[9], recvBytes[10] }; string username = Encoding.UTF8.GetString(bytes2).TrimEnd('\0'); #region --心跳-- if (userOnLineCounter.ContainsKey(username)) { userOnLineCounter[username] = 0; } else { userOnLineCounter.Add(username, 0); } #endregion byte[] array2 = new byte[100]; Buffer.BlockCopy(recvBytes, 11, array2, 0, 100); string text = Encoding.UTF8.GetString(array2).TrimEnd(new char[1]); string currPath = Application.StartupPath; //检查是否存在文件夹 string path = currPath + "\\userFiles\\" + username + "\\"; if (false == System.IO.Directory.Exists(path)) { //创建pic文件夹 System.IO.Directory.CreateDirectory(path); } else { } text = path + text; bool flag2 = File.Exists(text); if (flag2) { FileStream fileStream = File.Open(text, FileMode.Append); byte[] array3 = new byte[8192]; Buffer.BlockCopy(recvBytes, 111, array3, 0, 8192); fileStream.Write(array3, 0, 8192); fileStream.Close(); } else { FileStream fileStream2 = File.Create(text); byte[] array4 = new byte[8192]; Buffer.BlockCopy(recvBytes, 111, array4, 0, 8192); fileStream2.Write(array4, 0, 8192); fileStream2.Close(); } } break; case MessagePicks.OffLine: user.name = entity.NickName; _TCPuserList.Remove(user); BroadTCPCastUsers(); break; case MessagePicks.CarryMsg: var a = _TCPuserList.Find(entity.receiveClient); string audioJson = JsonConvert.SerializeObject(entity, Formatting.None); byte[] byteArray = System.Text.Encoding.UTF8.GetBytes("$" + audioJson + "*"); a.client.Send(byteArray); break; default: break; } } #endregion #region --UDP监听-- private System.Collections.ArrayList arr = new System.Collections.ArrayList(); private System.Collections.ArrayList arr2 = new System.Collections.ArrayList(); StringBuilder sb2 = new StringBuilder(); private void Run() { byte[] msgBuffer = null; while (true) { try { msgBuffer = _server.Receive(ref _remotePoint); //接受消息 int des = 0; if (msgBuffer[0] == 0x10 && msgBuffer[1] == 0x10) { des = 1; byte[] byteName = new byte[9]; byteName[0] = msgBuffer[2]; byteName[1] = msgBuffer[3]; byteName[2] = msgBuffer[4]; byteName[3] = msgBuffer[5]; byteName[4] = msgBuffer[6]; byteName[5] = msgBuffer[7]; byteName[6] = msgBuffer[8]; byteName[7] = msgBuffer[9]; byteName[8] = msgBuffer[10]; string name = System.Text.Encoding.UTF8.GetString(byteName); User user5 = _userList.Find(name.TrimEnd('\0')); arr.Capacity = 30; byte[] bytes1 = msgBuffer; arr.Add(bytes1); byte[] newbytes = (byte[])arr[0]; arr.RemoveAt(0); //messageID = DateTime.Now.ToString(); //int len = SendByte.Length; int totalSize = newbytes.Length; //***********小于mtu的数据包做标记直接发送**************** //如果总包超过udp的最大传输自己即MTU,则进行分包 if (totalSize < 1) { //数据小于60000字节,进行标记,方便Client接收 byte[] b1 = new byte[totalSize + 1]; b1[0] = 1; //给小于MTU的数据包做标记 Array.Copy(newbytes, 0, b1, 1, totalSize); _server.Send(b1, b1.Length, user5.NetPoint); } //**************大于mtu的数据包进行分包,然后发送********** else { //分包的大小 int PacketSize = 1000; //分包的数量 int PacketCount = 0; //分包个数的控制 //totalSize/PacketSize*PacketSize 只有两中情况,等于totalsize 或者是大于 if (totalSize / PacketSize * PacketSize < totalSize) { PacketCount = totalSize / PacketSize + 1; } else { PacketCount = totalSize / PacketSize; } int start = 0; //数据包 byte[] data = new byte[PacketSize + 4]; //开始循环发送数据包 for (int i = 0; i < PacketCount - 1; i++) { //第一个字节表示包的大小,第二字节表示第几个包,第三、四个字节 data[0] = Convert.ToByte(PacketCount); data[1] = (byte)i; data[2] = 0x10; //表示第几个包 data[3] = 0x10; //填充数据包;从bytes源数组提取数据。 Array.Copy(newbytes, start, data, 4, data.Length - 4); start += PacketSize; _server.Send(data, data.Length, user5.NetPoint); } //最后一个包的发送 //if (i == PacketCount - 1) //{ //最后一个包的大小 ,总长减掉之前拷贝的数据长度 //int LastDataPacket = (int)(totalSize - ((long)(PacketSize * i))); int LastDataPacket = (int)(totalSize - start); //最后一个包剩余的大小 ,加上报头长度 //data = new byte[LastDataPacket + 4]; data = new byte[LastDataPacket + 6]; //最后一个数据包的起始位置 //int LastDataPacketBegin = totalSize - LastDataPacket; //int LastDataPacketBegin = i * PacketSize; int LastDataPacketBegin = start; //给最后一个数据包的前两个自己打标 //data[0] = 0; data[0] = Convert.ToByte(PacketCount); data[1] = (byte)(PacketCount - 1); data[2] = 0x10; data[3] = 0x10; //将最后一个数据包的大小放入两个byte中 data[4] = (byte)(LastDataPacket / 256); data[5] = (byte)(LastDataPacket % 256); //data[4]=(byte) (LastDataPacket>>16);//平移16位 //data[5]=(byte) (LastDataPacket>>24); Array.Copy(newbytes, LastDataPacketBegin, data, 6, LastDataPacket); _server.Send(data, data.Length, user5.NetPoint); //System.Threading.Thread.Sleep(5); } } if (msgBuffer[0] == 0x10 && msgBuffer[1] == 0x11) { des = 1; byte[] byteName = new byte[9]; byteName[0] = msgBuffer[2]; byteName[1] = msgBuffer[3]; byteName[2] = msgBuffer[4]; byteName[3] = msgBuffer[5]; byteName[4] = msgBuffer[6]; byteName[5] = msgBuffer[7]; byteName[6] = msgBuffer[8]; byteName[7] = msgBuffer[9]; byteName[8] = msgBuffer[10]; string name = System.Text.Encoding.UTF8.GetString(byteName); User user5 = _userList.Find(name.TrimEnd('\0')); arr2.Capacity = 30; byte[] bytes1 = msgBuffer; arr2.Add(bytes1); byte[] newbytes = (byte[])arr2[0]; arr2.RemoveAt(0); //messageID = DateTime.Now.ToString(); //int len = SendByte.Length; int totalSize = newbytes.Length; //***********小于mtu的数据包做标记直接发送**************** //如果总包超过udp的最大传输自己即MTU,则进行分包 if (totalSize < 1) { //数据小于60000字节,进行标记,方便Client接收 byte[] b1 = new byte[totalSize + 1]; b1[0] = 1; //给小于MTU的数据包做标记 Array.Copy(newbytes, 0, b1, 1, totalSize); _server.Send(b1, b1.Length, user5.NetPoint); } //**************大于mtu的数据包进行分包,然后发送********** else { //分包的大小 int PacketSize = 1000; //分包的数量 int PacketCount = 0; //分包个数的控制 //totalSize/PacketSize*PacketSize 只有两中情况,等于totalsize 或者是大于 if (totalSize / PacketSize * PacketSize < totalSize) { PacketCount = totalSize / PacketSize + 1; } else { PacketCount = totalSize / PacketSize; } int start = 0; //数据包 byte[] data = new byte[PacketSize + 4]; //开始循环发送数据包 for (int i = 0; i < PacketCount - 1; i++) { //第一个字节表示包的大小,第二字节表示第几个包,第三、四个字节 data[0] = Convert.ToByte(PacketCount); data[1] = (byte)i; data[2] = 0x10; //表示第几个包 data[3] = 0x11; //填充数据包;从bytes源数组提取数据。 Array.Copy(newbytes, start, data, 4, data.Length - 4); start += PacketSize; _server.Send(data, data.Length, user5.NetPoint); //System.Threading.Thread.Sleep(5); } //最后一个包的发送 //if (i == PacketCount - 1) //{ //最后一个包的大小 ,总长减掉之前拷贝的数据长度 //int LastDataPacket = (int)(totalSize - ((long)(PacketSize * i))); int LastDataPacket = (int)(totalSize - start); //最后一个包剩余的大小 ,加上报头长度 //data = new byte[LastDataPacket + 4]; data = new byte[LastDataPacket + 6]; //最后一个数据包的起始位置 //int LastDataPacketBegin = totalSize - LastDataPacket; //int LastDataPacketBegin = i * PacketSize; int LastDataPacketBegin = start; //给最后一个数据包的前两个自己打标 //data[0] = 0; data[0] = Convert.ToByte(PacketCount); data[1] = (byte)(PacketCount - 1); data[2] = 0x10; data[3] = 0x11; //将最后一个数据包的大小放入两个byte中 data[4] = (byte)(LastDataPacket / 256); data[5] = (byte)(LastDataPacket % 256); //data[4]=(byte) (LastDataPacket>>16);//平移16位 //data[5]=(byte) (LastDataPacket>>24); Array.Copy(newbytes, LastDataPacketBegin, data, 6, LastDataPacket); _server.Send(data, data.Length, user5.NetPoint); } } if (des == 0) { sb2.Append(MidStrEx(System.Text.Encoding.UTF8.GetString(msgBuffer), "$", "*")); MessageEntity entity = (MessageEntity)JsonConvert.DeserializeObject<MessageEntity>(sb2.ToString()); sb2.Clear(); AnalysisPackage(entity, null); } } catch (Exception ex) { GC.Collect(); GC.WaitForPendingFinalizers(); EmptyWorkingSet(Process.GetCurrentProcess().Handle.ToInt32()); } } } #endregion #region -udp解析包-- private void AnalysisPackage(MessageEntity entity, User user2) { switch (entity.MessageType) { case MessagePicks.OffLine: User userOffLine = _userList.Find(entity.NickName); userOnLineCounter.Remove(entity.NickName); _userList.Remove(userOffLine); for (int i = 0; i < _TCPuserList.Count; i++) { if (_TCPuserList[i].name == entity.NickName) { _TCPuserList[i].client.Close(); _TCPuserList.RemoveAt(i); } } this.DoUserChanged(_userList); BroadCastUsers(); BroadTCPCastUsers(); break; case MessagePicks.RequestVedio: User user5 = _userList.Find(entity.receiveClient); MessageEntity entity5 = new MessageEntity(); entity5.MessageType = MessagePicks.RequestVedio; entity5.NickName = entity.NickName; entity5.MessageContent = entity.MessageContent; this.SendMessage(entity5, user5.NetPoint); break; case MessagePicks.AcceptVedio: User user6 = _userList.Find(entity.receiveClient); MessageEntity entity6 = new MessageEntity(); entity6.MessageType = MessagePicks.AcceptVedio; entity6.NickName = entity.NickName; entity6.MessageContent = entity.MessageContent; this.SendMessage(entity6, user6.NetPoint); break; case MessagePicks.Draw: User user7 = _userList.Find(entity.receiveClient); MessageEntity entity7 = new MessageEntity(); entity7.MessageType = MessagePicks.Draw; entity7.NickName = entity.NickName; entity7.receiveClientVideoBytes = entity.receiveClientVideoBytes; entity7.drawPoints = entity.drawPoints; entity7.MRordinates_upLeft = entity.MRordinates_upLeft; entity7.MRordinates_upRight = entity.MRordinates_upRight; entity7.MRordinates_downLeft = entity.MRordinates_downLeft; entity7.MRordinates_downRight = entity.MRordinates_downRight; entity7.VedioHeight = entity.VedioHeight; entity7.VedioWidth = entity.VedioWidth; string drawJson = JsonMapper.ToJson(entity7); //MessageEntity aa = JsonMapper.ToObject<MessageEntity>(drawJson); byte[] drawByte = System.Text.Encoding.UTF8.GetBytes("$" + drawJson + "*"); _server.Send(drawByte, drawByte.Length, user7.NetPoint); break; case MessagePicks.ScreenHeightWidth: User user9 = _userList.Find(entity.receiveClient); MessageEntity entity9 = new MessageEntity(); entity9.MessageType = MessagePicks.ScreenHeightWidth; entity9.NickName = entity.NickName; entity9.VedioHeight = entity.VedioHeight; entity9.VedioWidth = entity.VedioWidth; this.SendMessage(entity9, user9.NetPoint); break; case MessagePicks.Heartbeat: syncHeartbeatSend.Set(); if (userOnLineCounter.ContainsKey(entity.NickName)) { userOnLineCounter[entity.NickName] = 0; } else { userOnLineCounter.Add(entity.NickName, 0); } break; default: break; } } #endregion #region --用户登录广播-- public void BroadCastUsers() { MessageEntity entity = new MessageEntity(); entity.MessageType = MessagePicks.OnLine; userCollection.Clear(); for (int i = 0; i < _userList.Count; i++) { if (userCollection.ContainsKey(_userList[i].UserName) == false) { userCollection.Add(_userList[i].UserName, _userList[i].NetPoint.ToString()); } } entity.MessageContentEx = userCollection; foreach (User u in _userList) { SendMessage(entity, u.NetPoint); } } public void BroadTCPCastUsers() { MessageEntity entity = new MessageEntity(); entity.MessageType = MessagePicks.OnLine; Dictionary<string, string> MessageContentEx = new Dictionary<string, string>(); foreach (TCPuser user in _TCPuserList) { MessageContentEx.Add(user.name, user.name); } entity.MessageContentEx = MessageContentEx; string msgJson = JsonConvert.SerializeObject(entity, Formatting.None); byte[] Array = System.Text.Encoding.UTF8.GetBytes("$" + msgJson + "*"); foreach (TCPuser user in _TCPuserList) { if (user.client.Connected == true) { user.client.Send(Array); } } } #endregion #region --发送方法-- /// <summary> /// 发送消息 /// </summary> public void SendMessage(MessageEntity msg, IPEndPoint remoteIP) { //DoWriteLog("正在发送消息:" + msg.ToString()); if (msg == null) return; string msgJson = ""; msgJson = JsonConvert.SerializeObject(msg, Formatting.None); byte[] buffer = System.Text.Encoding.UTF8.GetBytes("$" + msgJson + "*"); _server.Send(buffer, buffer.Length, remoteIP); //DoWriteLog("消息已发送."); } public void Unity_SendMessage(MessageEntity msg, IPEndPoint remoteIP) { // DoWriteLog("正在发送消息:" + msg.ToString()); if (msg == null) return; string msgJson = JsonMapper.ToJson(msg); byte[] buffer = new byte[msg.receiveClientVideoBytes.Length + 1]; buffer = msg.receiveClientVideoBytes; buffer[buffer.Length - 1] = 0x11; _server.Send(buffer, buffer.Length, remoteIP); // DoWriteLog("消息已发送."); } public void TCP_SendMessage(MessageEntity msg) { string msgJson = JsonMapper.ToJson(msg); byte[] Array = System.Text.Encoding.UTF8.GetBytes("$" + msgJson + "*"); foreach (TCPuser user in _TCPuserList) { if (user.name == msg.receiveClient) { byte[] picslen = new byte[1]; picslen = System.Text.Encoding.UTF8.GetBytes(msg.VedioHeight.ToString()); byte[] filename = new byte[100]; //username = System.Text.Encoding.UTF8.GetBytes(msg.receiveClient); for (int i = 0; i < System.Text.Encoding.UTF8.GetBytes(msg.NickName).Length; i++) { filename[i] = System.Text.Encoding.UTF8.GetBytes(msg.NickName)[i]; } filename = copybyte(picslen, filename); byte[] buff = msg.receiveClientVideoBytes; buff = copybyte(filename, buff); MemoryStream ms = new MemoryStream(buff); while (true) { byte[] buffer = new byte[1024 * 10]; int len = ms.Read(buffer, 0, buffer.Length); if (len <= 0) break; user.client.Send(buffer); } user.client.Send(ASCIIEncoding.Default.GetBytes("|E|")); msg = null; ms.Close(); ms.Dispose(); //user.writer.Write(Array); //user.writer.Flush(); } } } #endregion #region --停止服务-- public void Stop() { DoWriteLog("停止服务器..."); try { _serverThread.Abort(); _server.Close(); DoWriteLog("服务器已停止."); } catch (Exception ex) { DoWriteLog("停止服务器发生错误: " + ex.Message); throw ex; } } #endregion #region --工具方法-- public byte[] copybyte(byte[] a, byte[] b) { byte[] c = new byte[a.Length + b.Length]; a.CopyTo(c, 0); b.CopyTo(c, a.Length); return c; } public string MidStrEx(string sourse, string startstr, string endstr) { string result = string.Empty; int startindex, endindex; try { startindex = sourse.IndexOf(startstr); if (startindex == -1) return result; string tmpstr = sourse.Substring(startindex + startstr.Length); endindex = tmpstr.IndexOf(endstr); if (endindex == -1) return result; result = tmpstr.Remove(endindex); } catch (Exception ex) { } return result; } #endregion }
}
客户端代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using vjsdn.net.library;
using System.Windows.Forms;
using System.IO;
using Common;
using Newtonsoft.Json;
using System.Linq;
namespace vjsdn.net.library
{
///
/// 客户端业务类
///
public class Client : IDisposable
{
//private const int MAX_RETRY_SEND_MSG = 1; //打洞时连接次数,正常情况下一次就能成功
private UdpClient _client;//客户端监听器 public IPEndPoint _hostPoint; //主机IP public IPEndPoint _remotePoint; //接收任何远程机器的数据 private UserCollection _userList;//在线用户列表 private Thread _listenThread; //监听线程 private string _LocalUserName; //本地用户名 //private bool _HoleAccepted = false; //A->B,接收到B用户的确认消息 private WriteLogHandle _OnWriteMessage; public WriteLogHandle OnWriteMessage { get { return _OnWriteMessage; } set { _OnWriteMessage = value; } } private UserChangedHandle _UserChangedHandle = null; public UserChangedHandle OnUserChanged { get { return _UserChangedHandle; } set { _UserChangedHandle = value; } } private CallChangedHandle _CallChangedHandle = null; public CallChangedHandle OnCallChanged { get { return _CallChangedHandle; } set { _CallChangedHandle = value; } } private ReceiveCallChangedHandle receiveCallChangedHandle = null; public ReceiveCallChangedHandle OnReceiveChanged { get { return receiveCallChangedHandle; } set { receiveCallChangedHandle = value; } } private ReceivePicChangedHandle _receivePicChangedHandle = null; public ReceivePicChangedHandle OnReceivePicChanged { get { return _receivePicChangedHandle; } set { _receivePicChangedHandle = value; } } private ReceiveAudioChangedHandle _receiveAudioChangedHandle = null; public ReceiveAudioChangedHandle OnReceiveAudioChanged { get { return _receiveAudioChangedHandle; } set { _receiveAudioChangedHandle = value; } } private ReceiveFileChangedHandle _receiveFileChangedHandle = null; public ReceiveFileChangedHandle OnReceiveFileChanged { get { return _receiveFileChangedHandle; } set { _receiveFileChangedHandle = value; } } private ReceiveDrawChangedHandle _receiveDrawChangedHandle = null; public ReceiveDrawChangedHandle OnReceiveDrawChanged { get { return _receiveDrawChangedHandle; } set { _receiveDrawChangedHandle = value; } } private ReceiveScreenSizeChangedHandle _receiveScreenSizeChangedHandle = null; public ReceiveScreenSizeChangedHandle OnReceiveScreenSizeChanged { get { return _receiveScreenSizeChangedHandle; } set { _receiveScreenSizeChangedHandle = value; } } /// <summary> ///显示跟踪记录 /// </summary> /// <param name="log"></param> private void DoWriteLog(string log) { if (_OnWriteMessage != null) (_OnWriteMessage.Target as Control).Invoke(_OnWriteMessage, log); } /// <summary> /// 构造器 /// </summary> /// <param name="serverIP"></param> public Client() { string serverIP = this.GetServerIP(); _remotePoint = new IPEndPoint(IPAddress.Any, 0); //任何与本地连接的用户IP地址。 _hostPoint = new IPEndPoint(IPAddress.Parse(serverIP), 5000); //服务器地址 _client = new UdpClient();//不指定端口,系统自动分配 _userList = new UserCollection(); _listenThread = new Thread(new ThreadStart(Run)); } /// <summary> /// 获取服务器IP,INI文件内设置 /// </summary> /// <returns></returns> private string GetServerIP() { string file = Application.StartupPath + "\\ip.ini"; string ip = File.ReadAllText(file); return ip.Trim(); } /// <summary> /// 启动客户端 /// </summary> public void Start() { if (this._listenThread.ThreadState == ThreadState.Unstarted) { this._listenThread.Start(); } } /// <summary> /// 客户登录 /// </summary> /// string ip = ""; public void Login(string userName, string password) { _client.Connect(_hostPoint); ip = password; _LocalUserName = userName; MessageEntity messageEntity = new MessageEntity(); messageEntity.NickName = userName; messageEntity.MessageType = MessagePicks.OnLine; messageEntity.MessageContent = string.Empty; ; this.SendMessage(messageEntity, _hostPoint); } /// <summary> /// 登出 /// </summary> public void Logout() { //C2S_LogoutMessage lgoutMsg = new C2S_LogoutMessage(_LocalUserName); //this.SendMessage(lgoutMsg, _hostPoint); //this.Dispose(); //System.Environment.Exit(0); } /// <summary> /// 发送请求获取用户列表 /// </summary> public void DownloadUserList() { //C2S_GetUsersMessage getUserMsg = new C2S_GetUsersMessage(_LocalUserName); //this.SendMessage(getUserMsg, _hostPoint); } /// <summary> /// 显示在线用户 /// </summary> /// <param name="users"></param> private void DisplayUsers(UserCollection users) { if (_UserChangedHandle != null) (_UserChangedHandle.Target as Control).Invoke(_UserChangedHandle, users); } /// <summary> /// 通话请求 /// </summary> /// <param name="users"></param> private void Call(string friend) { if (_CallChangedHandle != null) (_CallChangedHandle.Target as Control).Invoke(_CallChangedHandle, friend); } /// <summary> /// 接受通话请求 /// </summary> /// <param name="users"></param> private void ReceiveCall(string friend,string accept) { if (receiveCallChangedHandle != null) (receiveCallChangedHandle.Target as Control).Invoke(receiveCallChangedHandle, friend,accept); } /// <summary> /// 接受视频 /// </summary> /// <param name="users"></param> private void ReceivePic(byte[] pic) { if (_receivePicChangedHandle != null) (_receivePicChangedHandle.Target as Control).Invoke(_receivePicChangedHandle, pic); } private void ReceiveAudio(byte[] aud) { if (_receiveAudioChangedHandle != null) (_receiveAudioChangedHandle.Target as Control).Invoke(_receiveAudioChangedHandle, aud); } private void ReceiveFile(byte[] file) { if (_receiveFileChangedHandle != null) (_receiveFileChangedHandle.Target as Control).Invoke(_receiveFileChangedHandle, file); } private void ReceiveDraw(List<List<Pointt>> drawPoints,int whetherReceive3d) { if (_receiveDrawChangedHandle != null) (_receiveDrawChangedHandle.Target as Control).Invoke(_receiveDrawChangedHandle, drawPoints, whetherReceive3d); } private void ReceiveScreenSize(float width,float height) { if (_receiveScreenSizeChangedHandle != null) (_receiveScreenSizeChangedHandle.Target as Control).Invoke(_receiveScreenSizeChangedHandle, width, height); } public string MidStrEx(string sourse, string startstr, string endstr) { string result = string.Empty; int startindex, endindex; try { startindex = sourse.IndexOf(startstr); if (startindex == -1) return result; string tmpstr = sourse.Substring(startindex + startstr.Length); endindex = tmpstr.IndexOf(endstr); if (endindex == -1) return result; result = tmpstr.Remove(endindex); } catch (Exception ex) { } return result; } //运行线程 private int m_intBlockLength = 1000; //每一个数据包的大小 byte[] data = new byte[20000]; byte[] dataVioce = new byte[20000]; private void Run() { try { byte[] buffer; int i = 0; int j = 0; int i_voice = 0; int j_voice = 0; while (true) { try { buffer = _client.Receive(ref _remotePoint);//_remotePoint变量返回当前连接的用户IP地址 int des = 0; if (buffer[2] == 0x10 && buffer[3] == 0x10) { i = buffer[1]; int c = buffer[0]; if (i==0 && data[0]>0) { try { byte[] vediobyte = data.Skip(11).Take(data.Length).ToArray(); ReceivePic(vediobyte); //buffer = null; } catch (Exception ex) { MessageBox.Show(ex.ToString()); j = 0; } } //之前的包 if (buffer[0] != buffer[1] + 1) { Array.Copy(buffer, 4, data, i * (m_intBlockLength), m_intBlockLength); //复制数据 if(i>j) { continue; } j += 1; if (j > c | j == c) { j = i + 1; } } else { //最后一个包 int packesize = buffer[4] * 256 + buffer[5]; Array.Copy(buffer, 6, data, i * (m_intBlockLength), packesize); //复制数据 最后一个包 data.Length-packesize j += 1; if (j > c) { j = c; } } //if (buffer[0] == j) //{ // j = 0; // try // { // byte[] vediobyte = data.Skip(11).Take(data.Length).ToArray(); // ReceivePic(vediobyte); // //buffer = null; // } // catch (Exception ex) // { // MessageBox.Show(ex.ToString()); // j = 0; // } //} des = 1; } if (buffer[2] == 0x10 && buffer[3] == 0x11) { des = 1; i_voice = buffer[1]; int c = buffer[0]; if (i_voice == 0 && dataVioce[0] > 0) { try { byte[] bytelen = new byte[4]; bytelen[0] = dataVioce[11]; bytelen[1] = dataVioce[12]; bytelen[2] = dataVioce[13]; bytelen[3] = dataVioce[14]; int len = BitConverter.ToInt32(bytelen, 0); byte[] audiobyte = dataVioce.Skip(15).Take(len).ToArray(); ReceiveAudio(audiobyte); DoWriteLog("收到语音数据长度" + buffer.Length); } catch (Exception ex) { MessageBox.Show(ex.ToString()); j_voice = 0; } } //之前的包 if (buffer[0] != buffer[1] + 1) { Array.Copy(buffer, 4, dataVioce, i_voice * (m_intBlockLength), m_intBlockLength); //复制数据 if (i_voice > j_voice) { continue; } j_voice += 1; if (j_voice > c | j_voice == c) { j_voice = i_voice + 1; } } else { //最后一个包 int packesize = buffer[4] * 256 + buffer[5]; Array.Copy(buffer, 6, dataVioce, i_voice * (m_intBlockLength), packesize); //复制数据 最后一个包 data.Length-packesize j_voice += 1; if (j_voice > c) { j_voice = c; } } } if (buffer[2] == 0x10 && buffer[3] == 0x13) { } if (des == 0) { string str = System.Text.Encoding.UTF8.GetString(buffer); string ss = MidStrEx(str, "$", "*"); MessageEntity entity = (MessageEntity)JsonConvert.DeserializeObject<MessageEntity>(ss); AnalysisPackage(entity, null); } } catch (Exception ex) { //buffer=null; //i = 0; j = 0; } } } catch (Exception ex) { DoWriteLog(ex.Message); } } #region 接收服务器返回的包并分析处理 private void AnalysisPackage(MessageEntity entity, User user) { try { switch (entity.MessageType) { case MessagePicks.OnLine: if (entity.MessageContentEx.Count > 0) { _userList.Clear(); foreach (KeyValuePair<string, string> kvp in entity.MessageContentEx as Dictionary<string, string>) { ListViewItem lvi = new ListViewItem(); lvi.Text = kvp.Key; lvi.Tag = kvp.Value; User a = new User(kvp.Key, null); _userList.Add(a); this.DisplayUsers(_userList); } } break; case MessagePicks.Message: DoWriteLog(entity.MessageContent); // txtMessage.Append(entity.NickName + "【" + entity.RemoteEndPoint + "】 say:\n" + entity.MessageContent); break; case MessagePicks.OffLine: break; case MessagePicks.Vedio: byte[] bytes = entity.receiveClientVideoBytes; ReceivePic(bytes); break; case MessagePicks.FileOnLine: byte[] filebytes = entity.receiveClientVideoBytes; ReceiveFile(filebytes); break; case MessagePicks.AcceptVedio: if (entity.MessageContent.ToString() == "1") //对方已接听 ,开始发视频语音流 { ReceiveCall(entity.NickName,"1"); } else if (entity.MessageContent.ToString() == "0") //对方拒绝接听 { ReceiveCall(entity.NickName,"0"); } break; case MessagePicks.RequestVedio: //对方是否接听 this.Call(entity.NickName); break; case MessagePicks.Draw: ReceiveDraw(entity.drawPoints, 1); break; case MessagePicks.ScreenHeightWidth: ReceiveScreenSize((float)entity.VedioWidth, (float)entity.VedioHeight); break; default: break; } } catch (Exception ex) { } } #endregion #region IDisposable 成员 public void Dispose() { try { this._listenThread.Abort(); this._client.Close(); } catch { } } #endregion public void SendMessage(MessageEntity msg, IPEndPoint remoteIP) { if (msg == null) return; // DoWriteLog("正在发送消息给->" + remoteIP.ToString() + ",内容:" + msg.ToString()); string msgJson = JsonConvert.SerializeObject(msg, Formatting.None); byte[] buffer = System.Text.Encoding.UTF8.GetBytes("$" + msgJson + "*"); _client.Send(buffer, buffer.Length); DoWriteLog("消息已发送."); } public byte[] copybyte(byte[] a, byte[] b) { byte[] c = new byte[a.Length + b.Length]; a.CopyTo(c, 0); b.CopyTo(c, a.Length); return c; } public void SendBufferMessage(byte[] msg, IPEndPoint remoteIP) { _client.Send(msg, msg.Length); } public void SendMediaMessage(MessageEntity msg, IPEndPoint remoteIP) { if (msg == null) return; byte[] username = new byte[9]; //username = System.Text.Encoding.UTF8.GetBytes(msg.receiveClient); for (int i = 0; i < System.Text.Encoding.UTF8.GetBytes(msg.receiveClient).Length; i++) { username[i] = System.Text.Encoding.UTF8.GetBytes(msg.receiveClient)[i]; } byte[] result = new byte[msg.receiveClientVideoBytes.Length + 2 + username.Length]; byte[] b = new byte[2]; b[0] = 0x10; b[1] = 0x10; result = copybyte(b, username); result = copybyte(result, msg.receiveClientVideoBytes); _client.Send(result, result.Length); DoWriteLog("消息已发送."); } public void SendAudioMessage(MessageEntity msg, IPEndPoint remoteIP) { if (msg == null) return; byte[] username = new byte[9]; //username = System.Text.Encoding.UTF8.GetBytes(msg.receiveClient); for (int i = 0; i < System.Text.Encoding.UTF8.GetBytes(msg.receiveClient).Length; i++) { username[i] = System.Text.Encoding.UTF8.GetBytes(msg.receiveClient)[i]; } byte[] result = new byte[msg.receiveClientVideoBytes.Length + 2 + username.Length]; byte[] b = new byte[2]; b[0] = 0x10; b[1] = 0x11; result = copybyte(b, username); byte[] lenData = new byte[4]; for (int i = 0; i < BitConverter.GetBytes(msg.receiveClientVideoBytes.Length).Length; i++) { lenData[i] = BitConverter.GetBytes(msg.receiveClientVideoBytes.Length)[i]; } BitConverter.GetBytes(msg.receiveClientVideoBytes.Length); result = copybyte(result, lenData); result = copybyte(result, msg.receiveClientVideoBytes); _client.Send(result, result.Length); DoWriteLog("消息已发送."); } }
}
如果有需要源码可以找我,800块钱一套
微信:zhengzujoe