//粘包处理 public static bool StickyDeal(SocketAsyncEventArgs e) { AsyncUserToken token = (AsyncUserToken)e.UserToken; //复制数据 //e.Buffer中的数据要等到下一次调用异步接收时才会自动清除 //所以一次异步接收后,只用复制一次数据就行,这里要想办法规避掉第二次复制 if (!token.isCopy) { byte[] data = new byte[e.BytesTransferred]; Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred); Console.WriteLine("data的大小 : " + data.Count()); //将数据放入receiveBuffer //receiveBuffer是list<byte>类型的 lock (token.receiveBuffer) { token.receiveBuffer.AddRange(data); } token.isCopy = true; } //粘包处理 //接收到的数据长度还不足以分析包的类型和长度,跳出,让程序继续接收 TODO break->return? if (token.receiveBuffer.Count < 8) { return(false); } //如果packageLen长度为0,就得到包长 else if (token.packageLen == 0) { //得到包的类型 byte[] typeByte = token.receiveBuffer.GetRange(0, 4).ToArray(); token.packageType = BitConverter.ToInt32(typeByte, 0); //得到包的长度 byte[] lenBytes = token.receiveBuffer.GetRange(4, 8).ToArray(); token.packageLen = BitConverter.ToInt32(lenBytes, 0); } //接收到的数据长度不够,跳出,让程序继续接收 TODO 先分析包的类型再决定如何做? if (token.receiveBuffer.Count() < token.packageLen) { return(false); } //接收到的数据包最少有一个完整的数据,可以交给下面的函数处理 else { return(true); } }
//异步接收操作完成时调用此方法 //如果远程主机关闭了连接,那么就关闭套接字 //接收到了数据,将数据返回给客户端 private void ProcessReceive(SocketAsyncEventArgs e) { AsyncUserToken token = (AsyncUserToken)e.UserToken; try { //检查这个远程主机是否关闭连接 if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { //使用MessageDeal类处理数据 //TODO 在有大量数据包发送过来时,因为忙着处理数据库,所以有数据没有接收到 //这里当数据大于8且不知道长度时,说明还有数据可以分析,那么就继续分析 //如果剩下的包足够长,那么数据就会被处理,不够长,那么下一次剩余数据到达,必然会调用一次这个函数,也能处理 do { MessageDeal.ReceiveDeal(e); }while (token.receiveBuffer.Count > 8 && token.packageLen == 0); //Thread thread = new Thread(()=>MessageDeal.ReceiveDeal(e)); //thread.Start(); //这里每一次接收到数据后,就会调用发送函数的回调函数 //那么后面服务端自己主动发送的时候,就需要自己主动调用了 if (token.sendPacketNum.Count() > 0) { //调用发送函数的回调函数 ProcessSend(sendSAEA); } //接收完继续接收 Console.WriteLine("开始异步接收"); token.isCopy = false; bool willRaiseEvent = token.Socket.ReceiveAsync(e); if (!willRaiseEvent) { ProcessReceive(e); } } else { CloseClientSocket(); } } catch (Exception xe) { Console.WriteLine(xe.Message + "\r\n" + xe.StackTrace); } }
//异步接收操作完成时调用此方法 //如果远程主机关闭了连接,那么就关闭套接字 //接收到了数据,将数据返回给客户端 private void ProcessReceive(SocketAsyncEventArgs e) { AsyncUserToken token = (AsyncUserToken)e.UserToken; try { //检查这个远程主机是否关闭连接 if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { //增加服务器接收到的总字数 Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred); LogStringBuild.AppendFormat("The server has read a total of {0} bytes\n", m_totalBytesRead); LogString = LogStringBuild.ToString(); //处理数据 MessageDeal.ReceiveDeal(e); if (token.sendPacketNum.Count() > 0) { //调用发送函数的回调函数 ProcessSend(m_sendSaeaDic[token.Socket.RemoteEndPoint.ToString()]); } //将数据返回给客户端 //e.SetBuffer(e.Offset, e.BytesTransferred); //这里是从读取到发送 Console.WriteLine("开始异步接收"); bool willRaiseEvent = token.Socket.ReceiveAsync(e); if (!willRaiseEvent) { ProcessReceive(e); } } else { CloseClientSocket(e); } } catch (Exception xe) { Console.WriteLine(xe.Message + "\r\n" + xe.StackTrace); } }
//用来打包要发送的数据的函数,只需要传入数据类型,保存数据的字符串和发送数据所用的SAEA就行 public void SendMessage(int packageType, String str, SocketAsyncEventArgs e) { AsyncUserToken token = (AsyncUserToken)e.UserToken; //这里是要获得字节数而不是元素数 int packageLen = System.Text.Encoding.Default.GetByteCount(str) + 8; Console.WriteLine("包的大小为" + packageLen); byte[] bType = System.BitConverter.GetBytes(packageType); byte[] bLen = System.BitConverter.GetBytes(packageLen); //将数据放入发送buffer token.sendBuffer.AddRange(bType); token.sendBuffer.AddRange(bLen); token.sendBuffer.AddRange(System.Text.Encoding.Default.GetBytes(str)); //接下来可以调用发送函数的回调函数了 //下一次要发送多少数据 token.sendPacketNum.Add(packageLen); Console.WriteLine("数据装载完毕"); ProcessSend(sendSAEA); }
public static void UserInfoDeal(SocketAsyncEventArgs e) { MClient mClient = MClient.CreateInstance(); AsyncUserToken token = (AsyncUserToken)e.UserToken; //得到一个完整的包的数据,放入新list,第二个参数是数据长度,所以要减去8 List <byte> onePackage = token.receiveBuffer.GetRange(8, token.packageLen - 8); //将复制出来的数据从receiveBuffer旧list中删除 token.receiveBuffer.RemoveRange(0, token.packageLen); Console.WriteLine("清除receiveBuffer中的数据 , token.packageLen = " + token.packageLen); //list要先转换成数组,再转换成字符串 String jsonStr = Encoding.Default.GetString(onePackage.ToArray()); //得到用户名和密码 JObject obj = JObject.Parse(jsonStr); //如果传回来的用户信息是正确的 if (obj["isOk"].ToString().Equals("True")) { Console.WriteLine("保存自己的信息"); //先初始化数据库的静态变量(这里是用id合成数据库名) SqliteConnect.SqliteInit(obj["id"].ToString()); //然后创建好友表和信息表(如果有,不会重复创建) SqliteConnect.CreateTable(); //保存自己的最新信息到数据库 SqliteConnect.SaveUserInfo(obj["id"].ToString(), obj["UserName"].ToString(), obj["RealName"].ToString(), obj["Sex"].ToString(), obj["BirthDay"].ToString(), obj["Address"].ToString(), obj["Email"].ToString(), obj["PhoneNumber"].ToString(), obj["Remark"].ToString()); //主窗口隐藏 Application.Current.Dispatcher.Invoke(new Action(() => { Application.Current.MainWindow.Hide(); })); //打开好友界面 Application.Current.Dispatcher.Invoke(new Action(() => { FriendListWindow friedListWindow = new FriendListWindow(); friedListWindow.Show(); })); } else { MessageBox.Show("用户信息返回失败"); } }
//异步发送操作完成时调用此方法 //该方法在套接字上发出另一个接收以读取任何其他接收 ?? //从客户端发送的数据 // //<param name = "e"></param> private void ProcessSend(SocketAsyncEventArgs e) { //服务端的sendSAEA的Iocomplete没有绑定完成事件,所以服务端SendAsync后不会重复调用ProcessSend //而客户端的绑定了,如果不加下面的跳出函数,会一直循环。 //两种方法也说不上谁好,所以客户端先采用与服务端不同的方式,也许后面可以用于发送很大的数据 if (((AsyncUserToken)e.UserToken).sendBuffer.Count == 0) { return; } Console.WriteLine(e.SocketError); if (e.SocketError == SocketError.Success) { //用来将sendSAEA.UserToken中已经准备好的数据发送出去 AsyncUserToken token = (AsyncUserToken)e.UserToken; byte[] data; int count = token.sendBuffer.Count; //TODO 不能就这么莽撞地取出和发送所有数据,或许应该添加一个int类型的list,将下一次发送多少数据存入里面 //从sendBuffer中取出数据 data = token.sendBuffer.ToArray(); token.sendBuffer.Clear(); e.SetBuffer(data, 0, data.Length); Console.WriteLine("开始异步发送 datasize:" + data.Count() + "data:" + System.Text.Encoding.UTF8.GetString(data)); bool willRaiseEvent = token.Socket.SendAsync(e); if (!willRaiseEvent) { Console.WriteLine("调用了这个吗?"); ProcessSend(e); } Console.WriteLine("异步发送完毕"); } else { CloseClientSocket(); } }
public static void FriendInfoDeal(SocketAsyncEventArgs e) { MClient mClient = MClient.CreateInstance(); AsyncUserToken token = (AsyncUserToken)e.UserToken; //得到一个完整的包的数据,放入新list,第二个参数是数据长度,所以要减去8 List <byte> onePackage = token.receiveBuffer.GetRange(8, token.packageLen - 8); //将复制出来的数据从receiveBuffer旧list中删除 token.receiveBuffer.RemoveRange(0, token.packageLen); //list要先转换成数组,再转换成字符串 String jsonStr = Encoding.Default.GetString(onePackage.ToArray()); //得到用户名和密码 JArray jArray = JArray.Parse(jsonStr); if (jArray[0]["isOk"].ToString().Equals("True")) { Console.WriteLine("保存好友信息"); //保存还没有更新前的时间,如果时间比这个时间还晚,说明是已经被删除的好友 String updatetime = DateTime.Now.ToString(); foreach (var obj in jArray) { SqliteConnect.SaveFriendInfo(obj["id"].ToString(), obj["Group"].ToString(), obj["UserName"].ToString(), obj["RealName"].ToString(), obj["Sex"].ToString(), obj["BirthDay"].ToString(), obj["Address"].ToString(), obj["Email"].ToString(), obj["PhoneNumber"].ToString(), obj["Remarks"].ToString()); } //在保存完好友信息后,就要根据更新时间对数据库表中的数据进行排查,删除掉这一次还没有更新的数据 SqliteConnect.DeleteFriendByTime(updatetime); } else { //如果不进行这个补充,那么在只有一个好友,且服务端已经删除这个好友的情况下,客户端本地的该好友不会被删除 String updatetime = DateTime.Now.ToString(); SqliteConnect.DeleteFriendByTime(updatetime); } }
//创建一个未初始化的服务器实例 //开始监听 //先调用Init方法,然后调用Start方法 // //<param name = "numConnections">同时处理的最大连接数</param> //<param name = "receiveBufferSize">用于每个套接字操作的缓存区大小</param> private MClient(String ip, String port) { //初始化ip和port this.ip = ip; this.port = port; //实列化两个SAEA,分别用于接收和发送 this.readSAEA = new SocketAsyncEventArgs(); this.sendSAEA = new SocketAsyncEventArgs(); //TODO 分配缓存区 //绑定完成事件 this.readSAEA.Completed += new EventHandler <SocketAsyncEventArgs>(IO_Completed); this.sendSAEA.Completed += new EventHandler <SocketAsyncEventArgs>(IO_Completed); //分配信息保存空间 AsyncUserToken userToken = new AsyncUserToken(); this.readSAEA.UserToken = userToken; this.sendSAEA.UserToken = userToken; Console.WriteLine("MClient构造函数执行完毕"); }
//异步发送操作完成时调用此方法 //该方法在套接字上发出另一个接收以读取任何其他接收 ?? //从客户端发送的数据 // //<param name = "e"></param> private void ProcessSend(SocketAsyncEventArgs e) { if (e.SocketError == SocketError.Success) { //完成了将数据返回给客户端 AsyncUserToken token = (AsyncUserToken)e.UserToken; byte[] data; int count = token.sendBuffer.Count; //if ( count > 1024) //{ // data = token.sendBuffer.GetRange(0, 1024).ToArray(); // token.sendBuffer.RemoveRange(0, 1024); //} //else //{ // data = token.sendBuffer.GetRange(0, count).ToArray(); // token.sendBuffer.RemoveRange(0, count); //} data = token.sendBuffer.ToArray(); token.sendBuffer.Clear(); e.SetBuffer(data, 0, data.Length); Console.WriteLine("开始异步发送 datasize:" + data.Count() + "data:" + System.Text.Encoding.UTF8.GetString(data)); bool willRaiseEvent = token.Socket.SendAsync(e); if (!willRaiseEvent) { ProcessSend(e); } } else { CloseClientSocket(e); } }
public static void RegisMessDeal(SocketAsyncEventArgs e) { MClient mClient = MClient.CreateInstance(); AsyncUserToken token = (AsyncUserToken)e.UserToken; //得到一个完整的包的数据,放入新list,第二个参数是数据长度,所以要减去8 List <byte> onePackage = token.receiveBuffer.GetRange(8, token.packageLen - 8); //将复制出来的数据从receiveBuffer旧list中删除 token.receiveBuffer.RemoveRange(0, token.packageLen); //list要先转换成数组,再转换成字符串 String jsonStr = Encoding.Default.GetString(onePackage.ToArray()); //得到用户名和密码 JObject obj = JObject.Parse(jsonStr); if (obj["isRegist"].ToString().Equals("True")) { MessageBox.Show("注册成功"); //关闭注册窗口 MClientViewModel mClientViewModel = MClientViewModel.CreateInstance(); RegisterViewModel registerViewModel = RegisterViewModel.CreateInstance(); //重置输入框 registerViewModel.Resset(); //跨线程调用窗体组件的方法,使注册窗口关闭 mClientViewModel.registerWindow.Dispatcher.Invoke(new Action(() => { mClientViewModel.registerWindow.Close(); })); } else { MessageBox.Show("注册失败"); //清除掉密码 RegisterViewModel registerViewModel = RegisterViewModel.CreateInstance(); registerViewModel.PassWord = ""; registerViewModel.sPassWord = ""; } }
//对完整的数据进行分类处理 public static void ClassifyDeal(SocketAsyncEventArgs e) { AsyncUserToken token = (AsyncUserToken)e.UserToken; messageType type = (messageType)token.packageType; //根据数据类型处理数据 switch (type) { case messageType.landMessage: Console.WriteLine("登陆返回信息处理"); LandMessDeal(e); break; case messageType.registMessage: Console.WriteLine("注册返回信息处理"); RegisMessDeal(e); break; case messageType.UserInfo: Console.WriteLine("返回的该用户信息处理"); UserInfoDeal(e); break; case messageType.FriendInfo: Console.WriteLine("返回的好友信息处理"); FriendInfoDeal(e); break; case messageType.UserMessage: Console.WriteLine("返回的用户消息处理"); UserMessageDeal(e); break; case messageType.friendrequestMessage: Console.WriteLine("返回的用户请求处理"); FriendRequestMessageDeal(e); break; case messageType.AddFriendRetMessage: Console.WriteLine("返回的模糊搜索到的用户信息处理"); AddFriendRetMessage(e); break; case messageType.ChangeGroupMessage: Console.WriteLine("返回的模糊搜索到的用户信息处理"); ChangeGroupMessage(e); break; case messageType.DeleteFriendMessage: Console.WriteLine("返回的删除好友信息处理"); DeleteFriendMessage(e); break; case messageType.FriendStatusMessage: Console.WriteLine("返回的好友状态信息处理"); FriendStatusMessage(e); break; case messageType.ChangePassReturnMessage: Console.WriteLine("返回修改密码结果处理"); ChangePassReturnMessage(e); break; default: //可能的处理 break; } //将这两个标志归零 token.packageLen = 0; token.packageType = 0; }