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,用来同步接受服务器返回的代码。关于多线程的知识,请看我之前写的一个多线程监听小程序。
转载请标明出处。
c#用socket异步传输字符串的更多相关文章
- [转]C#网络编程(异步传输字符串) - Part.3
本文转自:http://www.tracefact.net/CSharp-Programming/Network-Programming-Part3.aspx 这篇文章我们将前进一大步,使用异步的方式 ...
- C#网络编程(异步传输字符串) - Part.3
这篇文章我们将前进一大步,使用异步的方式来对服务端编程,以使它成为一个真正意义上的服务器:可以为多个客户端的多次请求服务.但是开始之前,我们需要解决上一节中遗留的一个问题. 消息发送时的问题 这个问题 ...
- Windows下通过socket进行字符串和文件传输
今天在windows平台下,通过socket实现了简单的文件传输.通过实现这一功能,了解基本的windows网络编程和相关函数的使用方法. 在windows平台上进行网络编程,首先都需要调用函数WSA ...
- socket 发送字符串0x00时被截断
发送数据如下: aa 02 02 00 00 00 6f 6b 02 00 00 00 55 数据是以字符数组的形式(char msg[])存储发送的,send时发送长度填写的strlen(msg), ...
- TCP学习之五:客户端、服务端异步传输字符串
参考学习张子阳大神的博客:http://www.cnblogs.com/JimmyZhang/category/101698.html 消息发送接口: 消息接收接口: 客户端: 服务端: 消息发送类: ...
- socket详解
<?php /* * * socket主要翻译为套接字 * socket_accept — Accepts a connection on a socket * 接受一个socket链接 * s ...
- JAVA网络编程Socket常见问题 【长连接专题】
一. 网络程序运行过程中的常见异常及处理 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发生在服务器端进行new ...
- 基于 socket.io, 简单实现多平台类似你猜我画 socket 数据传输
一.前言 socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通 ...
- 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 ...
随机推荐
- php composer使用经验
1.使用composer引用了一个包,但是这个包没有使用命名空间,在项目中该如何使用这个包? 编辑composer.json文件 "autoload":{ "files& ...
- 用Navicat Premium 远程连接oracle数据库
1.安装Navicat Premium软件(我的是11.0.7版本)(假设安装路径为D:\NavicatLite\Navicat Premium) 2.下载 instantclient-basic-n ...
- poj 1099
http://poj.org/problem?id=1099 #include<stdio.h> #include<string.h> #include <iostrea ...
- Web Essentials之通用功能
返回Web Essentials功能目录 本篇目录 功能 快捷方式 WEIgnore 功能 通用功能应用于很多方面. 设置 所有的设置都可以在VS选择工具 -> 选项 -> Web Ess ...
- [.net 面向对象编程基础] (23) 结束语
[.net 面向对象编程基础] (23) 结束语 这个系列的文章终于写完了,用了半个多月的时间,没有令我的粉丝们失望.我的感觉就是一个字累,两个字好累,三个字非常累.小伙伴们看我每篇博客的时间就知道 ...
- MySQL--将MySQL数据导入到SQL Server
随着时代的进步,社会的发展,各种技术层出不穷五花八门乱七八糟数不胜数(写作文呢!!!) 不扯废话,简单而言,很多公司都会同时使用多种数据库,因此数据在不同数据库之间导入导出就成为一个让人蛋疼的问题,对 ...
- python _、__和__xx__的区别
python _.__和__xx__的区别 本文为译文,版权属于原作者,在此翻译为中文分享给大家.英文原文地址:Difference between _, __ and __xx__ in Pytho ...
- Ubuntu下安装 jdk6
Ubuntu下安装 jdk6 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公用协议 1,下载最新的 jdk6 版本,目前最 ...
- 可拖动的DIV续
之前写过一篇可拖动的DIV讲如何实现可拖动的元素,最后提出了几点不足,这篇文章主要就是回答着三个问题 1. 浏览器兼容性 2. 边界检查 3. 拖动卡顿.失灵 先附上上次代码 <!DOCTYPE ...
- 达洛克战记3 即将开服! What's New!
历经数个月的开发,达洛克战记3即将全新开服! 剧情: 回归到三大种族起源时期,三大种族并没有像现在三足鼎立.人类一直处于统治地位.但是突然间一群巨人的出现,让人类损失惨重,身为勇者,需要探索巨人背后的 ...