问题描述
namespaceAsyncTcpClient{publicpartialclassFormClient:Form{//是否正常退出privateboolisExit=false;//退出;false时,不退出privateTcpClientclient;//私人的tcpclient客户端privateBinaryReaderbr;//私人的读取br;privateBinaryWriterbw;BackgroundWorkerconnectWork=newBackgroundWorker();privatestringserverIP="222.31.143.219";publicFormClient(){InitializeComponent();this.StartPosition=FormStartPosition.CenterScreen;Randomr=newRandom((int)DateTime.Now.Ticks);//txt_UserName.Text="user"+r.Next(100,999);lst_OnlineUser.HorizontalScrollbar=true;connectWork.DoWork+=newDoWorkEventHandler(connectWork_DoWork);connectWork.RunWorkerCompleted+=newRunWorkerCompletedEventHandler(connectWork_RunWorkerCompleted);}///异步方式与服务器进行连接///</summary>///<paramname="sender"></param>///<paramname="e"></param>voidconnectWork_DoWork(objectsender,DoWorkEventArgse){client=newTcpClient();IAsyncResultresult=client.BeginConnect(serverIP,8889,null,null);while(!result.IsCompleted){Thread.Sleep(100);AddStatus(".");}try{client.EndConnect(result);e.Result="success";}catch(Exceptionex){e.Result=ex.Message;return;}}///异步方式与服务器完成连接操作后的处理///</summary>///<paramname="sender"></param>///<paramname="e"></param>voidconnectWork_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse){if(e.Result.ToString()=="success"){AddStatus("连接成功")NetworkStreamnetworkStream=client.GetStream();//将网络流作为二进制读写对象br=newBinaryReader(networkStream);bw=newBinaryWriter(networkStream);AsyncSendMessage("Login,"+txt_UserName.Text);ThreadthreadReceive=newThread(newThreadStart(ReceiveData));threadReceive.IsBackground=true;threadReceive.Start();}else{AddStatus("连接失败:"+e.Result);btn_Login.Enabled=true;}privatevoidbtn_Login_Click(objectsender,EventArgse){btn_Login.Enabled=false;AddStatus("开始连接.");connectWork.RunWorkerAsync();}///<summary>///处理接收的服务器收据///</summary>privatevoidReceiveData(){stringreceiveString=null;while(!isExit){ReceiveMessageDelegated=newReceiveMessageDelegate(receiveMessage);IAsyncResultresult=d.BeginInvoke(outreceiveString,null,null);//使用轮询方式来盘点异步操作是否完成while(!result.IsCompleted){if(isExit)break;Thread.Sleep(250);}//获取Begin方法的返回值所有输入/输出参数d.EndInvoke(outreceiveString,result);if(receiveString==null){if(!isExit)MessageBox.Show("与服务器失去联系");break;}string[]splitString=receiveString.Split(',');stringcommand=splitString[0].ToLower();switch(command){case"login"://格式:login,用户名AddOnline(splitString[1]);break;case"logout"://格式:logout,用户名RemoveUserName(splitString[1]);break;case"talk"://格式:talk,用户名,对话信息AddTalkMessage(splitString[1]+":rn");AddTalkMessage(receiveString.Substring(splitString[0].Length+splitString[1].Length+2));break;}}Application.Exit();///<summary>///发送信息状态的数据结构///</summary>privatestructSendMessageStates{publicSendMessageDelegated;publicIAsyncResultresult;}///<summary>///异步向服务器发送数据///</summary>///<paramname="message"></param>privatevoidAsyncSendMessage(stringmessage){SendMessageDelegated=newSendMessageDelegate(SendMessage);IAsyncResultresult=d.BeginInvoke(message,null,null);while(!result.IsCompleted){if(isExit)return;Thread.Sleep(50);}SendMessageStatesstates=newSendMessageStates();states.d=d;states.result=result;Threadt=newThread(FinishAsyncSendMessage);t.IsBackground=true;t.Start(states);///处理接收的服务端数据///</summary>///<paramname="obj"></param>privatevoidFinishAsyncSendMessage(objectobj){SendMessageStatesstates=(SendMessageStates)obj;states.d.EndInvoke(states.result);}delegatevoidSendMessageDelegate(stringmessage);///<summary>///向服务端发送数据///</summary>///<paramname="message"></param>privatevoidSendMessage(stringmessage){try{bw.Write(message);bw.Flush();}catch{AddStatus("发送失败");}}privatevoidbtn_SendeMessage_Click(objectsender,EventArgse){if(lst_OnlineUser.SelectedIndex!=-1){AsyncSendMessage("Talk,"+lst_OnlineUser.SelectedItem+","+rtf_SendMessage.Text+"rn");rtf_SendMessage.Clear();}elseMessageBox.Show("请先在[当前在线]中选择一个对话者");}delegatevoidConnectServerDelegate();///连接服务器///</summary>privatevoidConnectServer(){client=newTcpClient(serverIP,8889);}delegatevoidReceiveMessageDelegate(outstringreceiveMessage);///<summary>///读取服务器发过来的信息///</summary>///<paramname="receiveMessage"></param>privatevoidreceiveMessage(outstringreceiveMessage){receiveMessage=null;try{receiveMessage=br.ReadString();}catch(Exceptionex){AddStatus(ex.Message);}}privatedelegatevoidAddTalkMessageDelegate(stringmessage);///<summary>///向rtf中添加聊天///<paramname="message"></param>privatevoidAddTalkMessage(stringmessage){if(rtf_MessageInfo.InvokeRequired){AddTalkMessageDelegated=newAddTalkMessageDelegate(AddTalkMessage);rtf_MessageInfo.Invoke(d,newobject[]{message});}else{rtf_MessageInfo.AppendText(message);rtf_MessageInfo.ScrollToCaret();}}privatedelegatevoidAddStatusDelegate(stringmessage);///<summary>///向rtf中添加状态信息///</summary>///<paramname="message"></param>privatevoidAddStatus(stringmessage){if(rtf_StatusInfo.InvokeRequired){AddStatusDelegated=newAddStatusDelegate(AddStatus);rtf_StatusInfo.Invoke(d,newobject[]{message});}else{rtf_StatusInfo.AppendText(message);}}privatedelegatevoidAddOnlineDelegate(stringmessage);///<summary>///向lst_Online添加在线用户///</summary>///<paramname="message"></param>privatevoidAddOnline(stringmessage){if(lst_OnlineUser.InvokeRequired){AddOnlineDelegated=newAddOnlineDelegate(AddOnline);lst_OnlineUser.Invoke(d,newobject[]{message});}else{lst_OnlineUser.Items.Add(message);lst_OnlineUser.SelectedIndex=lst_OnlineUser.Items.Count-1;lst_OnlineUser.ClearSelected();}}privatedelegatevoidRemoveUserNameDelegate(stringuserName);///<summary>///从listBoxOnline删除离线用户///<paramname="userName"></param>privatevoidRemoveUserName(stringuserName){if(lst_OnlineUser.InvokeRequired){RemoveUserNameDelegated=RemoveUserName;lst_OnlineUser.Invoke(d,userName);}else{lst_OnlineUser.Items.Remove(userName);lst_OnlineUser.SelectedIndex=lst_OnlineUser.Items.Count-1;lst_OnlineUser.ClearSelected();}}privatevoidFormClient_FormClosing(objectsender,FormClosingEventArgse){if(client!=null){AsyncSendMessage("Logout,"+txt_UserName.Text);isExit=true;br.Close();bw.Close();client.Close();}}}}
解决方案
解决方案二:
很垃圾的软件设计,一堆while循环配上sleep(250),这是坑人的代码。有些人不知道如何设计并发多线程过程下的事件驱动机制,于是用“死循环+阻塞”,用一些Thread、AsyncXXXX之类的语法,其实去模拟顺序程序执行。对这类代码千万不要纠结它。
解决方案三:
其实一个好的并发多线程代码,它的代码数量要比这个少,那样的代码用集合明确的回调,简洁地联系起来几个事件,只要你能对异步概念入门就能看懂。而弄了一堆Thread.Sleep(...)这类代码的程序,让人在一大堆“死循环”之中去空想所谓的“多线程混乱争抢数据”的逻辑,这种所谓的异步其实是“相互牵制”的纠结概念,反而增加了代码复杂度。而且通常我们在随便测试、模拟一个耗时的程序——而又模拟不出真实代码——时,我们会写一个Sheep(1000)之类的代码。但是在你真实的去完成应用程序主要功能的代码中,怎么能有“故意拖延”的Sleep(...)语句呢?从这种代码出发点上看,这通常是作者毫无本领让一个通讯程序“正确地接收数据”所以才胡乱加上一些猜测出来的睡眠时间,这个Sleep语句中的参数值既不敢太高、又不敢不高,所以它是个“神经病数值”,不知道该取多少为宜。并且有了神经病数值,那么代码其它地方的逻辑也一定受它影响而多余出来、变得诡异难解。所以,假设我们辨认一下什么方式的编程设计趋向于稳定、容易理解、好维护,那么首先就是要删除Sleep(...)这类代码。只有这样才能“逼迫”一些人去真正写代码。
解决方案四:
那样的代码用集合明确的回调-->那样的代码用几个明确的回调