zoukankan      html  css  js  c++  java
  • 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,用来同步接受服务器返回的代码。关于多线程的知识,请看我之前写的一个多线程监听小程序。

    转载请标明出处。

  • 相关阅读:
    怎样装两个MySQL服务器
    MySQL 8.0.12的安装与卸载
    位运算符2
    位运算符
    赋值运算符
    love心形
    变量之间运算
    变量
    标识符
    算术运算符
  • 原文地址:https://www.cnblogs.com/likeFlyingFish/p/5345997.html
Copyright © 2011-2022 走看看