c#用socket异步传输字符串

再次特别感谢张子阳老师的文章,是我深感益处。
在前一篇文章中可以看到,尽管消息分成了三条单独发送,但是服务端却将后两条合并成了一条。对于这些情况,我们可以这样处理:就好像HTTP协议一样,在实际的请求和应答内容之前包含了HTTP头,其中是一些与请求相关的信息。我们也可以订立自己的协议,来解决这个问题,比如说,对于上面的情况,我们就可以定义这样一个协议:

[length=XXX]:其中xxx是实际发送的字符串长度(注意不是字节数组buffer的长度),那么对于上面的请求,则我们发送的数据为:“[length=25]Welcome to TraceFact.Net!”。而服务端接收字符串之后,首先读取这个“元数据”的内容,然后再根据“元数据”内容来读取实际的数据,它可能有下面这样两种情况:

NOTE:我觉得这里借用“元数据”这个术语还算比较恰当,因为“元数据”就是用来描述数据的数据。

“[“”]”中括号是完整的,可以读取到length的字节数。然后根据这个数值与后面的字符串长度相比,如果相等,则说明发来了一条完整信息;如果多了,那么说明接收的字节数多了,取出合适的长度,并将剩余的进行缓存;如果少了,说明接收的不够,那么将收到的进行一个缓存,等待下次请求,然后将两条合并。
“[”“]”中括号本身就不完整,此时读不到length的值,因为中括号里的内容被截断了,那么将读到的数据进行缓存,等待读取下次发送来的数据,然后将两次合并之后再按上面的方式进行处理。

转载请说明出处。

所以在此先拟定一个协议。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace SeverClass.RequestHanderSpace {
   public class RequestHander {
        private string temp = string.Empty;

        public string[] GetActualString(string input) {
            return GetActualString(input, null);
        }

        private string[] GetActualString(string input, List<string> OutputList) {
            if(OutputList==null)     OutputList = new List<string>();             
            if(!string.IsNullOrEmpty(temp))     input = temp +input;

            string output = "";
            string pattern = @"(?<=^[length=)(d+)(?=])";
            int length;
            
            if (Regex.IsMatch(input, pattern)) {
                Match m = Regex.Match(input, pattern);

                // get the length of string input;
                length = Convert.ToInt32(m.Groups[0].Value);

                //获取需要截取的位置
                int startIndex = input.IndexOf(']') + 1;
                
                //获取这个位置之后的字符串
                output = input.Substring(startIndex);

                if (length == output.Length) {
                    //如果output的长度与消息字符串的长度相等,说明刚好是一条消息;
                    OutputList.Add(output);
                    temp = "";
                } else if (output.Length < length) {
                    //说明截取之后的长度小于应有的长度,说明没有发完整,应将整条信息包括元数据全部缓存,与下一条数据一并处理。
                    temp = input;
                } else if (output.Length > length) {
                    //说明截取之后的长度大于应有的长度,说明消息发完整了,但是有多余的数据,多余的数据可能是截断信息,也可能是多条完整信息。
                    output = output.Substring(0, length);
                    OutputList.Add(output);
                    temp = "";

                    input = input.Substring(startIndex + length);//截取剩下的字符串
                    GetActualString(input, OutputList);//递归调用
                }
            } else {
                temp = input;//说明[]本身就不完整
            }
            return OutputList.ToArray();
        }
    }
}
对得到的字符串进行解析,在此用到了正则表达式,
string pattern = @"(?<=^[length=)(d+)(?=])";
关于正则表达式,请自行学习。

服务器代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using SeverClass.RequestHanderSpace;

 namespace SeverClass {
    public class RomoteClient {
        private TcpClient client;
        private NetworkStream streamToclient;
        private const int bufferSize = 8192;
        private byte[] buffer;
        private RequestHander hander;

        public RomoteClient(TcpClient client) {
            this.client = client;
            //the info of client connected
            Console.WriteLine("client connected{0} <-- {1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
            //获得流
            streamToclient = client.GetStream();
            buffer = new byte[bufferSize];

            hander = new RequestHander();
            //在构造函数中准备读取
            AsyncCallback callback = new AsyncCallback(ReadComplete);
            streamToclient.BeginRead(buffer, 0, bufferSize, callback, null);
        }

        //在读取完时进行回调
        private void ReadComplete(IAsyncResult ar) {
            int bytesRead = 0;
            try {
                lock (streamToclient) {
                    bytesRead = streamToclient.EndRead(ar);
                    Console.WriteLine("读取到{0}字节", bytesRead);
                }
                if (bytesRead == 0) {
                    throw new Exception("读取到0字节");
                }
                string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
                Array.Clear(buffer, 0, buffer.Length);//清空缓存,避免脏读
                string[] msgArray = hander.GetActualString(msg);//获取实际的字符串

                //遍历得到的字符串;
                foreach (string str in msgArray) {
                    Console.WriteLine("Recived: {0}", str);
                    Console.WriteLine();
                    string back = str.ToUpper();                  
                    back = string.Format("[length={0}]{1}",back.Length,back);
                    
                    //将得到的字符串转换为大写重新发送给客户端
                    byte[] send = Encoding.Unicode.GetBytes(back);
                    lock (streamToclient) {
                        streamToclient.Write(send, 0, send.Length);
                        streamToclient.Flush();
                    }
                    Console.WriteLine("Send: {0}", back);
                    Console.WriteLine();
                }
                //再次回调,无限循环,直到异常退出
                lock (streamToclient) {  
                    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);
            }
        }

    }
    class Program {
        static void Main(string[] args) {
            Console.WriteLine("Severe is running!");
            IPAddress ip = new IPAddress(new byte[4] { 127, 0, 0,1 });
            TcpListener listener = new TcpListener(ip, 8501);

            listener.Start();
            Console.WriteLine("Severe is listenning");

            while (true) {
                TcpClient client = listener.AcceptTcpClient();
                RomoteClient remote = new RomoteClient(client);
            }

            ConsoleKey key;
            while (Console.ReadKey().Key != ConsoleKey.Q) {
                continue;
            }
        }
    }
}

这是客户端代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using SeverClass.RequestHanderSpace;
using System.Threading;
namespace ClientClass {


    public class SeverClient {
        private const int bufferSize = 8192;
        private byte[] buffer;
        private TcpClient client;
        private NetworkStream streamToSever;
        private string msg = "welcome to tracefact .net";
        RequestHander hander = new RequestHander();




        public SeverClient() {
            try {
                client = new TcpClient();
                client.Connect("127.0.0.1", 8501);
            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
                return;
            }
            
            buffer = new byte[bufferSize];
            Console.WriteLine("连接成功! 
 {0}-->{1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
            streamToSever = client.GetStream();
            Thread readThread = new Thread(read);
            readThread.Start();
            
        }
        //send message to Sever
        public void sendMessage() {
            msg = string.Format("[length={0}]{1}", msg.Length, msg);
            
            for (int i = 0; i < 5; i++) {
                byte[] temp = Encoding.Unicode.GetBytes(msg);
                try {
                    lock (streamToSever) {
                        streamToSever.Write(temp, 0, temp.Length);
                        streamToSever.Flush();
                    }
                    Console.WriteLine("Send: {0}", msg);
                    Console.WriteLine();
                } catch (Exception ex) {
                    Console.WriteLine(ex.Message);
                    break;
                }
            }
        }
        public void read() {
            lock (streamToSever) {
                AsyncCallback callback = new AsyncCallback(ReadComplete);
                streamToSever.BeginRead(buffer, 0, bufferSize, callback, null);
            }
        }
        
       
        void ReadComplete(IAsyncResult ar) {
            int bytesRead = 0;
            try {
                lock (streamToSever) {
                    bytesRead = streamToSever.EndRead(ar);
                } if (bytesRead == 0) {
                    throw new Exception("读取到0字节");
                }
                Console.WriteLine("读取了{0}字节", bytesRead);
                string msg = Encoding.Unicode.GetString(buffer,0,bytesRead);
                
              
                Console.WriteLine(msg);
                string[] strArray = hander.GetActualString(msg);
                foreach (string str in strArray) {
                    Console.WriteLine("Recived: {0}", str);
                    Console.WriteLine();
                }
                               
                Array.Clear(buffer, 0, buffer.Length);//清空缓存 避免脏读


                lock (streamToSever) {
                    AsyncCallback callback = new AsyncCallback(ReadComplete);
                    streamToSever.BeginRead(buffer, 0, bufferSize, callback, null);
                }
            } catch (Exception ex) {
                if (streamToSever != null) {
                    streamToSever.Dispose();
                }
                client.Close();
                Console.WriteLine(ex.Message);
            }
        }
    }


    
    class Program {
        static void Main(string[] args) {
            SeverClient myClient = new SeverClient();
            myClient.sendMessage();
            Console.ReadLine();
        }
    }
}

在客户端,另建立了一个线程read,用来同步接受服务器返回的代码。关于多线程的知识,请看我之前写的一个多线程监听小程序。

转载请标明出处。

原文地址:https://www.cnblogs.com/likeFlyingFish/p/5345997.html