再次特别感谢张子阳老师的文章,是我深感益处。
在前一篇文章中可以看到,尽管消息分成了三条单独发送,但是服务端却将后两条合并成了一条。对于这些情况,我们可以这样处理:就好像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类集开发中一对多和多对多的关系的实现

    摘自<java开发实战经典>李兴华.著 一对多的关系 一个学校可以包含多个学生,一个学生属于一个学校,那么这就是一个典型的一对多关系,此时就可以通过类集进行关系的表示. 在定义Studen ...

  2. CCNA第二章TCP/IP简介考试要点学习笔记

    1.描述网络是如何工作的     DoD过程/应用层 -- OSI应用.表示和会话层(定义了结点到结点的应用通信协议以及对用户界面规范的控制): DoD主机到主机层 -- OSI传输层(保证了数据包的 ...

  3. 我的c漏洞

    传入指针参数 #include<stdio.h> #include<stdlib.h> void READ(int *a) { scanf("%d",a); ...

  4. Windows服务器安全加固实战(Windows Server 2008 R2和Windows Server 2012)

    最近我们立方技术工作室在使用阿里云的过程中,发现服务器安全性也不是很高,而服务端的安全软件都很贵.为了为朋友们提供更加有效的解决方案,我们决定身体力行,高筑墙,大幅度提升服务器的安全防护级别! 主机安 ...

  5. node.js初学遇到的问题

    是用express安装一个网站基础架构时 express -t ejs microblog 但是出来的模板引擎是jade,通过修改js也修改模板引用npm install 等等修改了index.ejs ...

  6. C++11新特性总结 (二)

    1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; ...

  7. 【菜鸟玩Linux开发】在C++里操作MySQL

    MySQL是一个的开源关系型数据库,对于服务端开发来说是一个优秀的选择.本篇内容将介绍如何在C++程序里操作MySQL数据库. ———————————————————————————————————— ...

  8. 【转】MySQL性能优化的21个最佳实践

    今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数据 ...

  9. Hadoop开发第4期---分布式安装

    一.复制虚拟机 由于Hadoop的集群安装需要多台机器,由于条件有限,我是用虚拟机通过克隆来模拟多台机器,克隆方式如下图所示

  10. 假期实践作业:从IT角度看地铁

    实习时间:2016/02/23——2016/02/26 实习地点:京港地铁14号线 实习报告: 大学四年过得真快,转眼就大三了,大学前两年半的生活可谓多姿多彩,从不懂计算机到对编程感兴趣,期待得最多的 ...