using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.Networking.Sockets; using System.IO; using Windows.Storage.Streams; using Windows.Networking; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography; namespace PlaneGcsSdk.Contract.EhNetUWP { public class PLNet { #region Properties public StreamOBJ MyStreamObj; /// /// 连接的IP /// public string ServerIP { get; protected set; } /// /// 连接的端口 /// public int TCPPort { get; protected set; } public bool IsClosing { get; protected set; } public bool IsOpen { get; protected set; } /// /// 真实名 /// public string MyNick { get; protected set; } /// /// 获取登录随机码 /// public string MyRND { get { return sMyTempRnd; } } /// /// 发送文件列表 /// public List SendFileList { get; protected set; } /// /// 接收文件列表 /// public List RevFileList { get; protected set; } /// /// 用户列表 /// public List FriendList { get; protected set; } /// /// 发送流列表 /// public List SendStreamList { get; protected set; } public List RevStreamList { get; protected set; } #endregion #region Fields string sMyTempRnd; //登录随机数 bool LoginFlag = false; //登录状态 string sMyID; //登录用户名 string sMyPassword; //登录密码 string sMyIP; //用户IP Encoding _enconding; int iStreamID = -1; int iFileID = 0; protected StreamSocket _socket; //连接对象 protected IInputStream _inputStream; protected Stream _outputStream; protected bool _canFlush = true; protected const uint READ_BUFFER_SIZE = 2047; #endregion #region Events public event EventHandler Login; public event EventHandler DiscoveredOneUser; public event EventHandler RefreshFriendListCompleted; public event EventHandler UserStateChange; public event EventHandler Disconnect; public event EventHandler CommandArrival; public event EventHandler ExceptionThrown; public event EventHandler StartSendFile; #endregion public PLNet() { FriendList = new List(); SendFileList = new List(); RevFileList = new List(); SendStreamList = new List(); RevStreamList = new List(); } #region Public Methods /// /// 建立连接 /// /// /// /// /// /// public async Task Start(string sServerIP, string sUsername, string sPassword, string md5Password = null) { Close(); IsClosing = false; if (_enconding == null) { _enconding = await DBCSEncoding.GetDBCSEncoding("GB2312"); } sMyID = sUsername; if (md5Password == null) sMyPassword = GetStrMd5Hash(sMyID + sPassword); else sMyPassword = md5Password; ServerIP = sServerIP; //登录用户的IP地址 TCPPort = 52510; //登录用户的端口 _socket = new StreamSocket(); //创建连接对象 await _socket.ConnectAsync(new HostName(ServerIP), TCPPort.ToString()).AsTask().ConfigureAwait(false); //执行连接操作 IsOpen = true; _inputStream = _socket.InputStream; _outputStream = _socket.OutputStream.AsStreamForWrite(); await Task.Run(() => TCPClientThread()); } /// /// 关闭连接 /// public void Close() { IsClosing = true; IsOpen = false; _inputStream?.Dispose(); _outputStream?.Dispose(); _socket?.Dispose(); if (SendFileList != null) SendFileList.Clear(); if (RevFileList != null) RevFileList.Clear(); Disconnect?.Invoke(this, EventArgs.Empty); //触发事件Disconnect } /// /// 发送命令 /// /// /// /// public async Task SendCommand(string friendIDs, string msg) { string commandtext = "*" + friendIDs + "|" + msg; await SendCmd(commandtext); } /// /// 发送数据流,给多个用户同时发送数据 /// /// /// /// public async Task SendStream(string friendIDs, byte[] myData) { string[] myFriends = friendIDs.Split(','); for (int i = 0; i < myFriends.Length; i++) { iStreamID += 1; MyStreamObj = new StreamOBJ(); MyStreamObj.StreamID = iStreamID; MyStreamObj.StreamData = myData; MyStreamObj.RevUserID = myFriends[i]; SendStreamList.Add(MyStreamObj); string streamtext = "*" + myFriends[i] + "|SB|" + MyStreamObj.StreamID.ToString() + "|" + MyStreamObj.StreamData.Length.ToString(); await SendCmd(streamtext); } } /// /// 发送普通文件 /// /// /// /// public async Task SendFileCommon(string friendID, string[] sFilenames) { string filetype = "F"; await SendFileCommon(friendID, sFilenames, filetype); } /// /// 发送图片文件 /// /// /// /// public async Task SendPicture(string friendID, string[] sFilenames) { string filetype = "P"; await SendFileCommon(friendID, sFilenames, filetype); } /// /// 发送视频文件 /// /// /// /// public async Task SendVideo(string friendID, string[] sFilenames) { string filetype = "V"; await SendFileCommon(friendID, sFilenames, filetype); } /// /// 发送文件公共函数 /// /// /// /// /// public async Task SendFileCommon(string friendID, string[] sFilenames, string filetype) { for (int i = 0; i < sFilenames.Length; i++) { FileOBJ tmpFileObj = new FileOBJ(); tmpFileObj.FileID = iFileID; iFileID += 1; FileInfo MyFileInfo = new FileInfo(sFilenames[i]); tmpFileObj.Filename = MyFileInfo.Name; tmpFileObj.FilePath = MyFileInfo.DirectoryName; tmpFileObj.FileLength = MyFileInfo.Length; tmpFileObj.FileModDate = filetype + MyFileInfo.LastWriteTime.ToString(); //filetype表示发送文件的类型 tmpFileObj.RevUserID = friendID; tmpFileObj.State = FileState.WaitConfim; tmpFileObj.tmpFileStream = new FileStream(sFilenames[i], FileMode.Open, FileAccess.Read); SendFileList.Add(tmpFileObj); StartSendFile?.Invoke(this, new StartSendFileEventArgs(SendFileList.Count - 1)); string filetext = "FB" + friendID + "|" + tmpFileObj.FileID.ToString() + "|" + tmpFileObj.Filename + "|" + tmpFileObj.FileLength.ToString() + "|" + tmpFileObj.FileModDate; await SendCmd(filetext); } } /// /// 发送数据公共函数 /// /// /// /// public async Task SendCmd(string cmdStr, byte[] addBytes = null) { byte[] tmpSendbytes = null; tmpSendbytes = _enconding.GetBytes(cmdStr); int nret; int strLen = LenB(cmdStr); if (addBytes == null) { tmpSendbytes = BitConverter.GetBytes((ushort)strLen); Array.Resize(ref tmpSendbytes, strLen + 2); nret = _enconding.GetBytes(cmdStr, 0, cmdStr.Length, tmpSendbytes, 2); } else { byte[] tmpBytes; long allLenth = strLen + addBytes.Length; tmpBytes = BitConverter.GetBytes(allLenth); Array.Resize(ref tmpBytes, strLen + 2); nret = _enconding.GetBytes(cmdStr, 0, cmdStr.Length, tmpBytes, 2); tmpSendbytes = AddByte(tmpBytes, addBytes); } try { if (tmpSendbytes != null) { await _outputStream.WriteAsync(tmpSendbytes, 0, tmpSendbytes.Length).ConfigureAwait(false); if (_canFlush) { await _outputStream.FlushAsync().ConfigureAwait(false); } } } catch (Exception ex) { _canFlush = false; Close(); ExceptionThrown?.Invoke(this, new ExceptionThrownEventArgs(ex)); } } #endregion #region Private Methods /// /// 启动一个接收数据的线程 /// private async void TCPClientThread() { uint i; //接收到的数据的长度 byte[] byteBuffer = null; //暂存接收到的数据 int comLength; bool ReadFlag = false; bool nRet; byte[] Tmpbyte; IBuffer tempbuf = new Windows.Storage.Streams.Buffer(READ_BUFFER_SIZE); //建立一个buffer await _inputStream.ReadAsync(tempbuf, READ_BUFFER_SIZE, InputStreamOptions.Partial); i = tempbuf.Length; byteBuffer = new byte[i]; tempbuf.CopyTo(byteBuffer); //将数据输出 while (i != 0) { if (byteBuffer.Length > 3) { comLength = BitConverter.ToUInt16(byteBuffer, 0); if (LoginFlag == false && comLength > 70) { #region if (byteBuffer.Length >= 2) { byte[] firstchar = new byte[1]; firstchar[0] = byteBuffer[1]; if (_enconding.GetString(firstchar) != "L") i = 0; } else { ReadFlag = true; } #endregion } else { #region if (byteBuffer.Length >= comLength + 1) { Tmpbyte = MidByte(byteBuffer, 3, comLength); //截取字节 if (Tmpbyte.Length < 4) nRet = false; nRet = await ProcessData(Tmpbyte); if (nRet == true) { byteBuffer = RightByte(byteBuffer, byteBuffer.Length - comLength - 2); //截取字节 if (byteBuffer.Length <= 0) ReadFlag = true; } else { return; } } else { ReadFlag = true; } #endregion } } else { ReadFlag = true; } if (ReadFlag == true) { #region ReadFlag = false; try { tempbuf = new Windows.Storage.Streams.Buffer(READ_BUFFER_SIZE); //建立一个buffer await _inputStream.ReadAsync(tempbuf, READ_BUFFER_SIZE, InputStreamOptions.Partial); i = tempbuf.Length; byte[] addbyte = new byte[i]; tempbuf.CopyTo(addbyte); byteBuffer = AddByte(byteBuffer, addbyte); } catch(Exception ex) { i = 0; } #endregion } } } /// /// 处理接收到的数据 /// /// /// private async Task ProcessData(byte[] tmpbyte) { string recestr = _enconding.GetString(tmpbyte); //这里本应是GBK编码 UserProperty tmpUserinfo = new UserProperty(); int tmpIndex; switch (recestr[0]) { case 'R': sMyTempRnd = recestr.Substring(1); await SendCmd("L" + sMyID + "|" + GetStrMd5Hash(sMyPassword + sMyTempRnd)); sMyTempRnd = sMyTempRnd.Substring(0, sMyTempRnd.Length - 1); break; case 'U': //收到用户信息 string sUserinfo = recestr.Substring(1); switch (sUserinfo[0]) { #region case 'A': //用户列表 #region sUserinfo = sUserinfo.Substring(1); tmpUserinfo.UserID = Fin(1, sUserinfo, "|"); tmpUserinfo.UserNick = Fin(2, sUserinfo, "|"); tmpUserinfo.UserIndex = FriendList.Count; tmpUserinfo.UserMessage = new List(); FriendList.Add(tmpUserinfo); DiscoveredOneUser?.Invoke(this, new DiscoveredOneUserEventArgs(tmpUserinfo)); #endregion break; case 'O': //用户上线 #region sUserinfo = sUserinfo.Substring(1); tmpIndex = GetUserIndexByID(Fin(1, sUserinfo, "|")); if (tmpIndex >= 0) { tmpUserinfo = FriendList[tmpIndex]; //修改用户在线状态 tmpUserinfo.UserOnline = true; FriendList[tmpIndex] = tmpUserinfo; UserStateChange?.Invoke(this, new UserStateChangeEventArgs(tmpUserinfo)); } #endregion break; case 'F': //用户下线 #region sUserinfo = sUserinfo.Substring(1); string tmpFriendID = Fin(1, sUserinfo, "|"); tmpIndex = GetUserIndexByID(tmpFriendID); if (tmpIndex >= 0) { tmpUserinfo = FriendList[tmpIndex]; tmpUserinfo.UserOnline = false; FriendList[tmpIndex] = tmpUserinfo; //处理发送文件残留 for (int i = 0; i < SendFileList.Count; i--) { if (SendFileList[i].RevUserID == tmpFriendID) { SendFileList[i].tmpFileStream.Dispose(); SendFileList.RemoveAt(i); } } for (int j = 0; j < RevFileList.Count; j--) { if (RevFileList[j].RevUserID == tmpFriendID) { RevFileList[j].tmpFileStream.Dispose(); RevFileList.RemoveAt(j); } } UserStateChange?.Invoke(this, new UserStateChangeEventArgs(tmpUserinfo)); } #endregion break; case 'D': break; case 'C': //被别人添加为好友时会收到 break; case 'E': RefreshFriendListCompleted?.Invoke(this, EventArgs.Empty); break; default: break; #endregion } break; case 'L': //登陆 recestr = recestr.Substring(1); switch (recestr[0]) { #region case 'P': Login?.Invoke(this, new LoginEventArgs(LoginState.LoginPwdError)); break; case 'O': LoginFlag = true; MyNick = recestr.Substring(1).Split('|')[0]; Login?.Invoke(this, new LoginEventArgs(LoginState.LoginOk)); await SendCmd("UG"); break; case 'U': Login?.Invoke(this, new LoginEventArgs(LoginState.LoginNoUser)); break; case 'E': Login?.Invoke(this, new LoginEventArgs(LoginState.LoginOtherError)); break; default: break; #endregion } break; case '*': //收到命令,格式为: *+FriendID+|+Msg string tmpStr = recestr.Substring(1); string tmpstr2 = Fin(2, tmpStr, "|"); switch (tmpstr2[0]) { #region case 'S': break; default: string friendid = Fin(1, tmpStr, "|"); string msgcontent = tmpStr.Substring(tmpStr.IndexOf("|") + 1); CommandArrival?.Invoke(this, new CommandArrivalEventArgs(friendid, msgcontent)); break; #endregion } break; default: break; } return true; } private int LenB(string str) { return _enconding.GetBytes(str).Count(); } /// /// 获取一个字节数组中的某些字节,从start个字节开始,获取length个字节,第几个是从1开始的 /// /// /// /// /// private byte[] MidByte(byte[] tbyte, int start, int length) { byte[] tmpMidByte = new byte[length]; //for (int i = 0; i < length; i++) //{ // tmpMidByte[i] = tbyte[i + start - 1]; //} for (int i = start - 1; i <= length - 1 + start - 1; i++) { tmpMidByte[i - (start - 1)] = tbyte[i]; } return tmpMidByte; } /// /// 获取某个字节数组的后面的字节,获取length个字节 /// /// /// /// private byte[] RightByte(byte[] tbyte, int length) { byte[] temRightByte = new byte[length]; temRightByte = MidByte(tbyte, tbyte.Length - length + 1, length); return temRightByte; } /// /// 获取某个字节数组的前面的字节,获取length个字节 /// /// /// /// private byte[] LeftByte(byte[] tbyte, int length) { byte[] temLeftByte = new byte[length]; temLeftByte = MidByte(tbyte, 1, length); return temLeftByte; } /// /// 拼接两个字节,将addbyte加在srcbyte后面 /// /// /// /// private byte[] AddByte(byte[] srcbyte, byte[] addbyte) { byte[] resultbyte = new byte[srcbyte.Length + addbyte.Length]; for (int i = 0; i < srcbyte.Length; i++) { resultbyte[i] = srcbyte[i]; } for (int j = 0; j < addbyte.Length; j++) { resultbyte[j + srcbyte.Length] = addbyte[j]; } return resultbyte; } /// /// 根据ID获取列表中序号 /// /// /// protected int GetUserIndexByID(string tmpUserID) { for (int i = 0; i < FriendList.Count; i++) { if (FriendList.ElementAt(i).UserID == tmpUserID) return i; } return -1; } /// /// 分割字符串,第Xnum个字符,字符串为SrcStr,分割特征为SpStr /// /// /// /// /// private string Fin(int xnum, string srcStr, string spStr) { string[] tmpStr; char[] FF = new char[1]; FF[0] = spStr[0]; tmpStr = srcStr.Split(FF); if ((xnum > 0) && (xnum <= tmpStr.Length)) return tmpStr[xnum - 1]; else return ""; } /// /// 计算字符串的MD5哈希值,输入为字符串strtohash,输出为字符串strHash /// /// /// private string GetStrMd5Hash(string strtohash) { byte[] tmpbyte = _enconding.GetBytes(strtohash); IBuffer buffMsg = CryptographicBuffer.CreateFromByteArray(tmpbyte); HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5); IBuffer tmpbuffer = objAlgProv.HashData(buffMsg); uint i = tmpbuffer.Length; byte[] mybyte = new byte[i]; tmpbuffer.CopyTo(mybyte); CryptographicBuffer.CopyToByteArray(tmpbuffer, out mybyte); StringBuilder sBuilder = new StringBuilder(); for (int k = 0; k < mybyte.Length; k++) { sBuilder.Append(mybyte[k].ToString("x2")); } string strHash = sBuilder.ToString(); return strHash; } #endregion } #region Nested Type /// /// 用户信息结构体 /// public struct UserProperty { public string UserID; public string UserNick; public string UserIP; public int UserIndex; public bool UserOnline; public List UserMessage; } /// /// 文件状态 /// public enum FileState { WaitConfim, StartSend, //开始发送,等待确认 Sending, //正在发送中 reSend, //断点续传中 SendError, //发送错误 Completed, //发送完成 LoginOtherError, //其他错 StartReveice, //开始接收,等待数据 Receiving, //接收中 ReReceiving, //断点续传中 ReceiveError } /// /// 登录状态 /// public enum LoginState { LoginPwdError, //密码错 LoginOk, //登陆成功 LoginNoUser, //用户不存在 LoginOtherError, //其他错 } /// /// 文件结构体 /// public struct FileOBJ { public string Filename; public string FilePath; public long FileLength; public string FileModDate; public int FileID; public string SendUserID; public string RevUserID; public FileStream tmpFileStream; public FileState State; } /// /// 数据流结构体 /// public struct StreamOBJ { public byte[] StreamData; public ulong CurrPos; public int StreamID; public ulong Length; public string SendUserID; public string RevUserID; } #endregion }