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

转载请标明出处。

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

  1. [转]C#网络编程(异步传输字符串) - Part.3

    本文转自:http://www.tracefact.net/CSharp-Programming/Network-Programming-Part3.aspx 这篇文章我们将前进一大步,使用异步的方式 ...

  2. C#网络编程(异步传输字符串) - Part.3

    这篇文章我们将前进一大步,使用异步的方式来对服务端编程,以使它成为一个真正意义上的服务器:可以为多个客户端的多次请求服务.但是开始之前,我们需要解决上一节中遗留的一个问题. 消息发送时的问题 这个问题 ...

  3. Windows下通过socket进行字符串和文件传输

    今天在windows平台下,通过socket实现了简单的文件传输.通过实现这一功能,了解基本的windows网络编程和相关函数的使用方法. 在windows平台上进行网络编程,首先都需要调用函数WSA ...

  4. socket 发送字符串0x00时被截断

    发送数据如下: aa 02 02 00 00 00 6f 6b 02 00 00 00 55 数据是以字符数组的形式(char msg[])存储发送的,send时发送长度填写的strlen(msg), ...

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

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

  6. socket详解

    <?php /* * * socket主要翻译为套接字 * socket_accept — Accepts a connection on a socket * 接受一个socket链接 * s ...

  7. JAVA网络编程Socket常见问题 【长连接专题】

    一. 网络程序运行过程中的常见异常及处理 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发生在服务器端进行new ...

  8. 基于 socket.io, 简单实现多平台类似你猜我画 socket 数据传输

    一.前言 socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通 ...

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

    原文链接: http://www.cnblogs.com/kenkofox/archive/2010/04/25/1719649.html 代码: http://files.cnblogs.com/k ...

随机推荐

  1. tar.xz文件解压

    原文:http://blog.csdn.net/rheostat/article/details/7614451 感谢CSDN的<帝都码农> ======================= ...

  2. day9---多线程,线程锁,队列

    进程.线程 http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 使用threading模块实现多线程编程[综述] Pyt ...

  3. 如何获得DataGrid中某行某列的对象

    假如某行是 Xm_struct x = this.Brow.SelectedItem as Xm_struct;则下面分别是第5和第七列的对象 TextBlock Ddjs = this.Brow.C ...

  4. 用脚本定时监控SQL Server主从一致性

    用脚本定时监控SQL Server主从一致性 首先说一下我们的环境 我们使用的是事务复制,复制是单向的,主服务器和从服务器都在同一个机房,当然不同机房也可以,只需要改一下IP和端口 下面的脚本在我们的 ...

  5. C# - JSON详解

    最近在做微信开发时用到了一些json的问题,就是把微信返回回来的一些json数据做一些处理,但是之前json掌握的不好,浪费了好多时间在查找一些json有关的转换问题,我所知道的方法只有把json序列 ...

  6. [.net 面向对象编程基础] (10) 类的成员(字段、属性、方法)

    [.net 面向对象编程基础] (10) 类的成员(字段.属性.方法) 前面定义的Person的类,里面的成员包括:字段.属性.方法.事件等,此外,前面说的嵌套类也是类的成员. a.类的成员为分:静态 ...

  7. node访问iis使用keep-alive设置不当

    iis默认的连接超时时间为2分钟 ,因此node程序使用keep-alive访问时,keep-alive的时间不应该超过2分钟,否则在请求完成后,node端继续保持连接,2分钟后iis断开连接,会导致 ...

  8. redis系列-redis的持久化

    redis对数据的持久化有两种方式:RDB(快照保存)和AOF(命令日志). RDB 介绍:将内存快照保存到磁盘,dump.rdb二进制文件 触发:满足“N 秒内数据集至少有 M 个改动”,或使用sa ...

  9. IOS Animation-CAShapeLayer、UIBezierPath与Animation的结合

    在阅读本文之前,对CAShapeLayer.UIBezierPath不熟悉的话,可以先阅读文章 贝塞尔曲线与Layer 如果对动画不熟悉的话,先阅读文章 动画基础.深入 Layer是绘图的画板,Bez ...

  10. Atitit 词法分析器的设计最佳实践说明attilax总结

    Atitit 词法分析器的设计最佳实践说明attilax总结 1.1. 手写的优点:代码可读,对源代码中的各种错误给出友好的提示信息,用户体验高,1 1.2. 使用状态表比较简单,dfa比较麻烦1 1 ...