再次特别感谢张子阳老师的文章,是我深感益处。
在前一篇文章中可以看到,尽管消息分成了三条单独发送,但是服务端却将后两条合并成了一条。对于这些情况,我们可以这样处理:就好像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. java-API中的常用类,新特性之-泛型,高级For循环,可变参数

    API中的常用类 System类System类包含一些有用的类字段和方法.它不能被实例化.属性和方法都是静态的. out,标准输出,默认打印在控制台上.通过和PrintStream打印流中的方法组合构 ...

  2. listview选中没有效果

    listview选中没有效果了,设置了android:listselector也没有效果,最后发现是listview中的item布局设置了背景颜色导致,把item的背景色去掉就OK了 http://b ...

  3. Apache Shiro (一)

    参考博客: http://jinnianshilongnian.iteye.com/blog/2018398 1.shiro简介 Apache shiro 是一个JAVA框架,可用于身份难和授权.sh ...

  4. JavaScript 基础第八天(DOM第二天)

    一.引言 初步认识DOM有可能会被各种不熟悉的因为因素影响自己的学习心态,你需要的是多去记忆一些单词然后加强自己的代码量. 二.导入 在昨天初步认识DOM以后我们见天将接着介绍有关于DOM的内容. 三 ...

  5. C语言-数据类型

    数据类型 -基本数据类型 --char 字符型 --int  整型 --浮点型 ---float ---double -指针类型 --void* -空类型 -构架类型 --数组[] --结构体 str ...

  6. Adobe Flash Builder 4.7破解方法(绝对可用)

    Flash Builder4.7 破解方法的实践 建议:作为草根一级,买不起正版,只能先拿破解版练练手了,如果商业的话,请用正版.  具体步骤如下: 1.到Adobe官网下载FlashBuilder  ...

  7. Android Meun 用法

    Android Meun 用法 点击菜单实体键弹出菜单:如下图 main_activity.xml <?xml version="1.0" encoding="ut ...

  8. No prohects are avaliable for deployment to this server

    没有项目可用于部署到该服务器的项目或者所有项目都已部署到该服务器或没有发现项目 报错的就是这样的信息,网上看了很多解决方案,比如:http://ttov.blog.163.com/blog/stati ...

  9. jmSlip WEB前端滑屏组件

    基于css3的滑屏组件 demo: http://slip.jm47.com 下载: https://github.com/jiamao/jmSlip 功能清单 区域横滚 整屏竖滚 滚动动画效果 区域 ...

  10. Courier-MTA 0.72 发布,电子邮件系统

    Courier-MTA 0.72 增加了 ratefilter,用来限制速率的邮件过滤器. Courier 是一个优秀的电子信件系统,功能上它远比 Qmail. Postfix 等我们已经熟知的系统完 ...