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("连接成功! \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,用来同步接受服务器返回的代码。关于多线程的知识,请看我之前写的一个多线程监听小程序。

转载请标明出处。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-06 17:56:43

c#用socket异步传输字符串的相关文章

C#网络编程(异步传输字符串)

C#网络编程(异步传输字符串) - Part.3 这篇文章我们将前进一大步,使用异步的方式来对服务端编程,以使它成为一个真正意义上的服务器:可以为多个客户端的多次请求服务.但是开始之前,我们需要解决上一节中遗留的一个问题. 消息发送时的问题 这个问题就是:客户端分两次向流中写入数据(比如字符串)时,我们主观上将这两次写入视为两次请求:然而服务端有可能将这两次合起来视为一条请求,这在两个请求间隔时间比较短的情况下尤其如此.同样,也有可能客户端发出一条请求,但是服务端将其视为两条请求处理.下面列出了

TCP学习之五:客户端、服务端异步传输字符串

参考学习张子阳大神的博客:http://www.cnblogs.com/JimmyZhang/category/101698.html 消息发送接口: 消息接收接口: 客户端: 服务端: 消息发送类: 消息接收类:

Html5之高级-14 Web Socket(概述、API、示例)

一.Web Socket 概述 Web Socket 简介 - Web Socket 是 HTML5 提供的在 Web应用程序中客户端与服务器端之间进行的非 HTTP 的通信机制 - Web Socket 实现了用 HTTP 不容易实现的服务器端的数据推送等智能通讯技术 Web Socket 的特点 - Web Socket 可以在服务器与客户端之间建立一个非 HTTP 的双向连接 - 这个连接时实时的,也是永久的 - 服务器端可以主动推送消息 - 服务器端不再需要轮询客户端的请求 - 服务器端

Linux socket 测试

Client : mini2440 Server : PC Ubuntu 网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.socket也有一个类似于打开文件的函数:socket(),调用socket(),该函数返回一个整型的socket的描述符,随后的连接建立.数据传输等操作也都是通过该socket实现.此次测试用mini2440做Client,PC机做Server,同时连接上局域网.ping通之后,在mini2440上运行client.c,PC上运行server.

java 和 C++ Socket通信(java作为服务端server,C++作为客户端client,解决中文乱码问题GBK和UTF8)

原文链接: http://www.cnblogs.com/kenkofox/archive/2010/04/25/1719649.html 代码: http://files.cnblogs.com/kenkofox/Client-CPlusPlus.rarhttp://files.cnblogs.com/kenkofox/Server_Java.rar java和C++使用Socket通信,其实底层Socket都是相通的,所以只需要按照各自的语法去做就是了. java服务器端使用ServerSo

使用 Python 进行 socket 编程

本文主要参考 https://docs.python.org/3/howto/sockets.html . 本文只讨论 STREAME(比如 TCP) INET(比如 IPv4) socket. 在多种跨进程通信方式中,sockets 是最受欢迎的.对于任意给定的平台,有可能存在其他更快的跨进程通信方式,但对于跨平台交流,sockets 应该是唯一的一种. 创建 Socket 客户端 Socket 通俗的讲,当你点击一个链接,你的浏览器会做以下事情: # create an INET, STRE

Python基础:网络编程socket基本篇

socket也叫套接字,是对各种协议的封装,实现收发数据. Python里socket工作过程:(图片来自网络) socket在Python中实际上是一个模块,实现发送和接收数据的功能. 因为socket是一个类,所以只导入模块需要使用socket.socket()创建一个socket对象. 创建一个socket格式: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) 参数名 选项名称 作用 family AF_UNIX

Java学习---TCP Socket的学习

基础知识 1. TCP协议 TCP是一种面向连接的.可靠的.基于字节流的运输层(Transport layer)通信协议.在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议. TCP所提供服务的主要特点:面向连接的传输:端到端的通信:高可靠性,确保传输数据的正确性,不出现丢失或乱序:全双工方式传输:采用字节流方式,即以字节为单位传输字节序列:紧急数据传送功能 TCP支持的服务:文件传送File Transfer:远程登录Remote login:

Python之路_Day10

Python之路_Day10_课堂笔记 上节回顾: socket: 1.导入模块 2.创建socket 3. 字节 send:每次发送可能没有完全发送,send发送后会有一个返回值,是本次发送了多少. sendall:循环发送,直到全部发送完全. 接收 recv(2048):最多可以接收2048字节,上传文件时需要注意 粘包: socketserver: 1.自定义类 2.继承socketserver.BaseRequestHandler类 3.重写handle方法 4.socketserver