异步传输字符串

来源:互联网 发布:python换两行 编辑:程序博客网 时间:2024/06/08 08:02

异步传输字符串

 

前面说过服务端的四种方式:

1.服务一个客户端的一个请求

2.服务一个客户端的多个请求

3.服务多个客户端的一个请求

4.服务多个客户端的多个请求

 

前面说到第三种了,对于最后一种最实际的情况前面说的是将内层的while循环交给一个新建的线程去让它来完成.

 

除了这种方式以外,还可以使用一张更好地方式----使用线程池中的线程来完成.可以使用BeginRead(),BeginWrite()等异步方法,同时让BeginRead()方法和它的回调方法形成一个类似于while的无限循环:首先在第一层循环中,接收到一个客户端后,调用BeginRead(),然后为该方法提供一个读取完成后的回调方法,接下来在毁掉方法中对收到的字符进行处理,随后在回调方法中接着调用BeginRead()方法,并传入回调方法本身.

 

服务端的实现:

 

using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading.Tasks; namespace Server{    class Program    {        static void Main(string[] args)        {            Console.WriteLine("Server is running...");            IPAddress ip = new IPAddress(new byte[] { 192,168,3,19});            TcpListener listener = new TcpListener(ip,9271);            listener.Start();//开始监听            Console.WriteLine("Start Listening...");            while (true)            {                //获取一个连接,同步方法,在此处中断                TcpClient client = listener.AcceptTcpClient();                Server wapper = new Server(client);            }        }    }     public class Server    {        private TcpClient client;        private NetworkStream streamToClient;        private const int BufferSize = 8192;        private byte[] buffer;        private RequestHandler handler;         public Server(TcpClient client)        {            this.client = client;             //打印连接到的客户端信息            Console.WriteLine("\nClient Connected! Local: {0}<--Client: {1}",client.Client.LocalEndPoint,client.Client.RemoteEndPoint);             //获得流            streamToClient = client.GetStream();            buffer = new byte[BufferSize];             //设置ResquestHandler            handler = new RequestHandler();             //在构造函数中就开始准备读取            AsyncCallback callBack = new AsyncCallback(ReadComplete);            streamToClient.BeginRead(buffer,0,BufferSize, callBack, null);         }        //在读取完成时进行回调        private void ReadComplete(IAsyncResult ar)        {            int bytesRead = 0;            try            {                bytesRead = streamToClient.EndRead(ar);                if (bytesRead==0)                {                    Console.WriteLine("Client offline");                    return;                }                string msg = Encoding.Unicode.GetString(buffer,0,bytesRead);                Array.Clear(buffer,0,buffer.Length);//清空缓存,避免脏读                string[] msgArray = handler.GetActualString(msg);//获得实际的字符串                 //遍历或得到的字符串                foreach (string m in msgArray)                {                    Console.WriteLine("Received: {0} [{1} bytes]",m,bytesRead);                     string back = m.ToUpper();                     //将得到的字符串改为大写并重新发送                    byte[] temp = Encoding.Unicode.GetBytes(back);                    streamToClient.Write(temp,0,temp.Length);                    streamToClient.Flush();                    Console.WriteLine("Sent: {0}",back);                }                //再次调用BeginRead(),完成时调用自身,形成无限循环                AsyncCallback callBack = new AsyncCallback(ReadComplete);                streamToClient.BeginRead(buffer,0,BufferSize,callBack,null);            }            catch (Exception ex)            {                if (streamToClient!=null)                {                    streamToClient.Dispose();                }                client.Close();                Console.WriteLine(ex.Message);                            }        }    }}

因为前面关于RequestHandler的代码说过了,这里就不说了.

 

 

客户端的实现

 

与服务端类似,TcpClient进行了一个简单的包装,使它的使用更加方便一下,将类命名为Client:

using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading.Tasks; namespace Client{    class Program    {        static void Main(string[] args)        {            ConsoleKey key;            Client c = new Client();            c.SendMessage("Hello,Thanks you for reading!!!");            Console.WriteLine("\n\n按Q退出");            do            {                key = Console.ReadKey(true).Key;            } while (key!=ConsoleKey.Q);        }    }     public class Client    {        private const int BufferSize = 8192;        private byte[] buffer;        private TcpClient client;        private NetworkStream streamToServer;         public Client()        {            try            {                client = new TcpClient();                client.Connect(new IPAddress(new byte[] { 192, 168, 3, 19 }), 9271);            }            catch (Exception ex)            {                Console.WriteLine(ex.Message);                return;            }            buffer = new byte[BufferSize];             //打印连接到的服务端信息            Console.WriteLine("Server Connected! Local: {0}-->Server: {1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);             streamToServer = client.GetStream();        }         //连续发送三条消息到服务端        public void SendMessage(string msg)        {            msg = string.Format("[length={0}]{1}", msg.Length, msg);             for (int i = 0; i < 3; i++)            {                byte[] temp = Encoding.Unicode.GetBytes(msg);//获得缓存                try                {                    streamToServer.Write(temp, 0, temp.Length);                    Console.WriteLine("Sent: {0}", msg);                }                catch (Exception ex)                {                    Console.WriteLine(ex.Message);                    break;                }            }            AsyncCallback callBack = new AsyncCallback(ReadComplete);            streamToServer.BeginRead(buffer,0,BufferSize,callBack,null);        }         //读取完成时的回调方法        private void ReadComplete(IAsyncResult ar)        {            int bytesRead;            try            {                bytesRead = streamToServer.EndRead(ar);                if (bytesRead == 0)                {                    Console.WriteLine("Server offline");                    return;                }                string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);                Console.WriteLine("Received: {0} [{1}bytes]", msg, bytesRead);                Array.Clear(buffer, 0, buffer.Length);                AsyncCallback callBack = new AsyncCallback(ReadComplete);                streamToServer.BeginRead(buffer, 0, BufferSize, callBack, null);             }            catch (Exception ex)            {                if (streamToServer!=null)                {                    streamToServer.Dispose();                }                client.Close();                Console.WriteLine(ex.Message);            }        }    }}


因为楼主为了大家看的明白,把所有的类都写在同一个文件中了,这样不利于阅读,不管怎么样,程序现在能跑起来了,看一下程序的运行效果:

//服务端:Server is running...Start Listening... Client Connected! Local: 192.168.3.19:9271<--Client: 192.168.3.19:29144Received: Hello,Thanks you for reading!!! [252 bytes]Sent: HELLO,THANKS YOU FOR READING!!!Received: Hello,Thanks you for reading!!! [252 bytes]Sent: HELLO,THANKS YOU FOR READING!!!Received: Hello,Thanks you for reading!!! [252 bytes]Sent: HELLO,THANKS YOU FOR READING!!! Client Connected! Local: 192.168.3.19:9271<--Client: 192.168.3.19:29147Received: Hello,Thanks you for reading!!! [252 bytes]Sent: HELLO,THANKS YOU FOR READING!!!Received: Hello,Thanks you for reading!!! [252 bytes]Sent: HELLO,THANKS YOU FOR READING!!!Received: Hello,Thanks you for reading!!! [252 bytes]Sent: HELLO,THANKS YOU FOR READING!!! 开启了两个客户端: //客户端1:Server Connected! Local: 192.168.3.19:29144-->Server: 192.168.3.19:9271Sent: [length=31]Hello,Thanks you for reading!!!Sent: [length=31]Hello,Thanks you for reading!!!Sent: [length=31]Hello,Thanks you for reading!!!  按Q退出Received: HELLO,THANKS YOU FOR READING!!! [62bytes]Received: HELLO,THANKS YOU FOR READING!!!HELLO,THANKS YOU FOR READING!!! [124bytes] //客户端2:Server Connected! Local: 192.168.3.19:29147-->Server: 192.168.3.19:9271Sent: [length=31]Hello,Thanks you for reading!!!Sent: [length=31]Hello,Thanks you for reading!!!Sent: [length=31]Hello,Thanks you for reading!!!  按Q退出Received: HELLO,THANKS YOU FOR READING!!! [62bytes]Received: HELLO,THANKS YOU FOR READING!!! [62bytes]Received: HELLO,THANKS YOU FOR READING!!! [62bytes]


 

看到有个客户端在接受消息的时候出现了问题了吗?客户端将来自服务端的两个消息合并成了一个消息.想想怎么样解决?

 

在客户端没有采取刚才我们编写的协议来处理,因此当客户端收到应答时,仍然会发生文本合并的情况.

 

使用这种定义协议的方式有它本身的优点,但缺点也很明显,如果客户端知道了这个协议,有意的输入[length=XXX],但是后面的长度却不匹配,此时程序就会出错.可选的解决办法是对”[”和”]”进行编码,当客户端有意输入这两个字符时,将它替换成”\[”和”\]”或者别的字符,在读取之后再将它还原.


 现在应该可以看出服务端可以连接多个客户端,同时可以处理他们的多次请求.


从实用性的角度看,这些示例不完善,在服务端,接收到字符串之后,将其转换为大写后立即发送给了客户端.流程总是:客户端-->服务端-->客户端.在实际中,他们之间的交互是无序的,可能客户端发送多条消息,服务端只回复一条消息.可以尝试将服务端改为使用Console.ReadLine()接受用户输入,然后在发回给客户端.

 

 

0 0
原创粉丝点击