C#网络编程 tcpclient (阻塞异步)

C# 高效的语言,既然选择了它来写网络服务器,就应该使用最简单的方式。

MS提供了SOCKET和TCPCLIENT两种方式给我们使用,TCPCLIENT封装了socket,似乎应该用tcpclient来搞。。。



关于阻塞:

该概念出现的场景是服务器端,在接受客户端连接的场景。我们在服务器端用TcpListener打开一个监听,等待客户端的连接,需要使用AcceptTcpClient()方法。

这个AcceptTcpClient()方法一旦使用,整个线程就停住(阻塞)了。  直到有客户端连接上来后,AcceptTcpClient()方法才返回客户端的连接。


关于异步:

该概念出现在网络连接已经建立好后的场景,服务器程序或客户端程序,从连接的数据流里面读取或写入数据,如果数据流中有数据才读取,程序代码跟着执行就是同步,

或者不管有无数据,都去试做读数据流中的数据,而使用回调函数来处理这摊子事情,就是异步。



基于TCPCLIENT,下面使用简单的代码来实现一个阻塞,异步的网络服务器。


服务器开始,使用一个线程来处理监听操作:


            Thread lsThread = new Thread(new ThreadStart(server_listen));
            lsThread.IsBackground = true;
            lsThread.Start();



        //////////////////
        /////网络监听////
        /////////////////
        private void server_listen()
        {
            listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 3000);
            listener.Start();

            while (true)
            {
                Thread.Sleep(1000);
                while (listener.Pending())  //跳开阻塞,用这个在关闭服务器程序时不会出现异常
                {
                    tcpuser one = new tcpuser();
                    one.tcp = listener.AcceptTcpClient();
                    
                    
                    Thread subThread = new Thread(new ParameterizedThreadStart(f1)); //线程
                    subThread.Start(one);
                    

                    
                  
                    gv.tcpuserlist.Add(one);  

                }
            }
        }

关于listener.Pending,这个方法可以测试listener中有没有客户端连接上来,如果有,再去接收客户端的连接。

这样的写法,就可以避免程序被直接使用AcceptTcpClient()方法阻塞住。




        //多客户端的处理线程
        private void f1(object xxx)
        {
            tcpuser tp = (tcpuser)xxx;
            NetworkStream stream = tp.tcp.GetStream();            

            while (true)
            {

                var begint = DateTime.Now;
                rwl.EnterReadLock(); //无论花费多长时间,线程都会一直等到获得锁为止
                

                try
                {
                        //对话逻辑处理 
                        client_business(tp);
                        
                        //网络写                        
                        if (!String.IsNullOrWhiteSpace(tp.txtout))
                        {
                            tp.writebuffer = Encoding.UTF8.GetBytes(tp.txtout + "."); //加一次消息结束符号.
                            if (tp.tcp != null && tp.tcp.Connected)
                            {
                                if (stream.CanWrite) { stream.BeginWrite(tp.writebuffer, 0, tp.writebuffer.Length, new AsyncCallback(WriteCallBack), tp); }
                            }
                            else { tp.islogon = 0; rwl.ExitReadLock(); break; }
                        }

                        
                        //网络读
                        if (tp.tcp != null && tp.tcp.Connected) { stream.BeginRead(tp.readbuffer, 0, tp.readbuffer.Length, new AsyncCallback(ReadCallBack), tp); }
                        else { tp.islogon = 0; rwl.ExitReadLock(); break; }
                     

                }
                catch { listBox2.Items.Insert(0, "对话模块 ERROR"); tp.islogon = 0; rwl.ExitReadLock(); break; }



                Thread.Sleep(100);                
                rwl.ExitReadLock();
                listBox1.Items.Insert(0, tp.playername + " client:" + Convert.ToInt32(DateTime.Now.Subtract(begint).TotalMilliseconds).ToString());             
                
            }

        }


上面的代码使用BeginRead来读取流中的数据,BeginRead方法是一个回调函数,WIN系统会处理函数中的代码,并且它自己结束时候,又调用了自己,就形成了一个循环,可以不停的处理数据。


下面是BeginRead函数的代码内容:


        //网络读子函数
        public void ReadCallBack(IAsyncResult ar)
        {
            try
            {
                tcpuser one = (tcpuser)ar.AsyncState;
                if (one.tcp != null && one.tcp.Connected)
                {
                    NetworkStream stream = (NetworkStream)one.tcp.GetStream();
                    int numberOfBytesRead = stream.EndRead(ar);
                    one.txtin = Encoding.UTF8.GetString(one.readbuffer, 0, numberOfBytesRead);
                 
                    if (!String.IsNullOrWhiteSpace(one.txtin))
                    { one.txtlonglong = one.txtlonglong + one.txtin;  one.txtin = ""; } //每次收到的,都放longlong

                }
            }
            catch { listBox2.Items.Insert(0, "ReadCallBack ERROR"); }
        }


        //网络写子函数
        public void WriteCallBack(IAsyncResult ar)
        {
            try
            {
                tcpuser one = (tcpuser)ar.AsyncState;
                if (one.tcp != null && one.tcp.Connected)
                {
                    NetworkStream stream = (NetworkStream)one.tcp.GetStream();
                    stream.EndWrite(ar);
                    one.txtout = "";
                }
            }
            catch { listBox2.Items.Insert(0, "WriteCallBack ERROR");  }
        }




















你可能感兴趣的:(黄巾之乱(网络游戏))