异步传输字符串

异步传输字符串

 

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

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:29144
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!!!
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:29147
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!!!
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:9271
Sent: [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:9271
Sent: [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()接受用户输入,然后在发回给客户端.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值