c#用socket异步传输字符串

来源:互联网 发布:手机淘宝怎么寄件 编辑:程序博客网 时间:2024/05/19 05:37

再次特别感谢张子阳老师的文章,是我深感益处。在前一篇文章中可以看到,尽管消息分成了三条单独发送,但是服务端却将后两条合并成了一条。对于这些情况,我们可以这样处理:就好像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("连接成功! \n {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,用来同步接受服务器返回的代码。关于多线程的知识,请看我之前写的一个多线程监听小程序。

转载请标明出处。

1 0
原创粉丝点击