C# 异步Socket (BeginXXXX)服务器代码

前言:

1、最近维护公司的一个旧项目,是Socket通讯的,主要用于接收IPC(客户端)发送上来的抓拍图像,期间要保持通讯,监测数据包并进行处理。但是看之前那人写的代码个人觉得并不是很适合自己,于是就重写了,不过项目暂时弃置了,为了以后能够方便使用,也方便更多像我一样还是渣渣程序员的人,记录一些心得。我还是坚信那句话,分享才能够进步得更快

2、其实在做之前我对这个东西了解也很少,毕竟以我的认识,在国内C#更多地是用来开发网站,于是也在网上看了很多前辈贴的代码,我尝试过直接复制粘贴,发现很多都用不了最后自己写了才发现原来是这么一回事。所以在这里也奉劝各位不要做伸手党,通过自己的理解才能够将别人的知识转变成为自己的资本

开始

1、Socket 通信分为同步和异步两种模式,客户端一般用同步,异步多用于服务端。至于两者的区别,很多前辈已经有博客阐述过了,我这里就贴其中一个链接帮助大家理解:http://www.cnblogs.com/BLoodMaster/archive/2010/07/02/1769774.html。万一这个链接失效,相信大家也会搜索到其他资源的

2、做这种Socket通信,我觉得关键点在于【数据处理】而不是建立链接,但往往很多不了解人会遇到各种困难,比如Socket链接关闭,丢包,粘包之类的。通用做法就是

  2.1:使用缓冲区,将接收到的字节数组全部储存起来,再去分析数据,获取有效部分。

  2.2:服务器跟客户端需要约定好报文的格式,一般来说至少要有【数据头标识】、【数据实体长度】、【数据尾标识】,这样才能更好地对数据进行处理,否则你很难界定到底哪一段数据才是你想要的。

  2.3:数据分析一定要少用字符串或者字符拆解,因为这样很有可能获取到的长度是错误的(里面有很多空字节,或者编码解码问题导致字符串长度不一致),所以一定要用字节数组来处理,通过约定好的编码解码格式,查找标识所在位置进行数据拆解。【比如:客户端跟服务器连接需要核对身份信息,因此对一字符串进行的特殊加密再编码发送,服务器接收到数据之后如果先编译成了字符串再获取密码转成byte[],长度跟之前记录的很可能就会不一样】。不过对于获取报文头信息这一类通过字符串来拆解更为方便

3、在建立连接时通常发生的问题就是没有循环接收客户端消息导致socket被释放,或者只接受到客户端了几条消息之后就停了,我也遇到过这个问题,所以建立连接的关键在于BeginAccept和BeginReceive两个方法的递归循环调用

4、接下来就是贴代码了,这里只有服务器的,客户端的我还没有做以后补全了会发上来,入口函数是公开的,主要方法全部在这里,至于辅助函数(比如获取IP地址,检查端口是否被占用、获取发送消息大家可以自己去找一下吧,毕竟这些已经有很多前辈开源了)

  4.1:由于本人只是一渣渣野生程序猿,如果大家有什么不同的看法请提出来,交流也是一种很好的进步方式

  4.2:如果大家觉得有用,还是希望能够推荐一下,让更多新人看到,让大家能够彼此学习,也不枉费我写博客的力气,谢谢

  4.3:我更希望有大神来告诉我不足的地方,因为我知道这些代码写得并不好~

public void Create_Server_Socket()
{
Thread th_server = null;
string result = string.Empty;
try
{
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = Pubilc.Helper.Computer.GetIPv4();
IPEndPoint point = new IPEndPoint(ip, this._port);
socketServer.Bind(point);
socketServer.Listen(this._listen);
th_server = new Thread(() =>
{
if (this._isRunning)
socketServer.BeginAccept(AcceptCallBack, socketServer);
else
Close_Server_Socket();
});
th_server.IsBackground = true;
th_server.Start();
}
catch (Exception ex)
{
if (th_server.IsAlive)
th_server.Abort();
ShowLog(ex.Message);
};
}
public void Close_Server_Socket()
{
if (socketServer != null)
{
socketServer.Close();
}
} void AcceptCallBack(IAsyncResult ar)
{
Socket socketServer = (Socket)ar.AsyncState;
try
{
Socket socketAccept = socketServer.EndAccept(ar);
DynamicBuffer state = new DynamicBuffer();
state.workSocket = socketAccept;
socketAccept.BeginReceive(state.Buffers, 0, state.Buffers.Length, SocketFlags.None, ReceiveCallBack, state);
}
catch(Exception ex)
{
LogHelper.WriteLog(typeof(SocketHelper_Server), ex);
ShowLog("AcceptCallBack" + ex.Message);
}
finally
{
if (this._isRunning)
socketServer.BeginAccept(AcceptCallBack, socketServer);
} }
void ReceiveCallBack(IAsyncResult ar)
{
string sendMsg = string.Empty;
DynamicBuffer state = (DynamicBuffer)ar.AsyncState;
Socket socketAccept = state.workSocket;
try
{
int len = socketAccept.EndReceive(ar);
if (len > 0)
{
state.WritBuffer();
}
else
{
sendMsg=doMsg.CheckConnection(socketAccept.RemoteEndPoint.ToString());
if (!string.IsNullOrEmpty(sendMsg))
{
byte[] buffer = Encoding.Default.GetBytes(sendMsg);
state.sb.AddRange(buffer);
}
else
{
socketAccept.Shutdown(SocketShutdown.Both);
socketAccept.Close();
}
}
}
catch (Exception ex)
{
LogHelper.WriteLog(typeof(SocketHelper_Server), ex);
ShowLog("ReceiveCallBack" + ex.Message);
}
finally
{
if (this._isRunning)
{
try
{
Thread th_send = null;
th_send = new Thread(() =>
{
Send(ref state);
th_send.Abort();
});
th_send.IsBackground = true;
th_send.Start();
socketAccept.BeginReceive(state.Buffers, 0, state.Buffers.Length, SocketFlags.None, ReceiveCallBack, state);
}
catch (Exception ex)
{
showLog(ex.Message);
LogHelper.WriteLog(typeof(IpcServerDo), ex);
}
}
}
}
void Send(ref DynamicBuffer state)
{
byte[] buffer = null;
string sendMsg = string.Empty;
Socket socketAccept = state.workSocket;
Dictionary<string, byte[]> data;
state.GetAllData(out data);
if (data.Count > 0)
{
sendMsg = doMsg.AcceptFromIpc(socketAccept.RemoteEndPoint.ToString(), ref data);
if (!string.IsNullOrEmpty(sendMsg))
{
buffer = Encoding.Default.GetBytes(sendMsg.ToCharArray());
socketAccept.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallBack, socketAccept);
}
Send(ref state);
}
}
void SendCallBack(IAsyncResult ar)
{
Socket socketAccept = (Socket)ar.AsyncState;
try
{
int sendBytes = socketAccept.EndSend(ar);
}
catch(Exception ex)
{
LogHelper.WriteLog(typeof(SocketHelper_Server), ex);
}
}

4、下面是State对象类,其实这个就是相当于缓冲区,将东西放到这里来保存,包括socket对象,其实有用的就是上面的声明部分和构造函数,至于下面的写入方法和数据拆解,缓冲区清理的方法我都觉得有待提高,因为用这种方法我在接收一张500KB左右的图片竟然要10秒才有返回结果(整个逻辑流程完成并发送数据的时间),这是不可以接受的,所以下面的数据处理方法,大家见仁见智吧,根据自己的需求去找到自己适合的方法,这里只是提供思路

/// <summary>
/// 循环队列
/// </summary>
/// <typeparam name="T"></typeparam>
public class DynamicBuffer
{
/// <summary>
/// 缓冲区大小
/// </summary>
public int bufferSize { get; set; }
/// <summary>
/// 通讯对象
/// </summary>
public Socket workSocket { get; set; }
/// <summary>
/// 缓冲区
/// </summary>
public byte[] Buffers { get; set; }
/// <summary>
/// 储存区
/// </summary>
public List<byte> sb;
public DynamicBuffer()
{
this.bufferSize = 1024 *64;
this.sb = new List<byte>();
this.Buffers = new byte[bufferSize];
}
/// <summary>
/// 将缓冲区数据放入储存区
/// </summary>
public void WritBuffer()
{
int endPosition = 0;
byte[] buffer;
int len = 0;
for (endPosition = this.Buffers.Length - 1; endPosition >= 0; endPosition--)
{
//由于发现数据中有很多\0的空字节,此处从最后开始执行反向删除
if (this.Buffers[endPosition] != (byte)'\0')
break;
}
len = endPosition + 1;//因为是取长度,而不是位置,因此此处+1
buffer = new byte[len];
Array.Copy(this.Buffers, 0, buffer, 0, len);
this.sb.AddRange(buffer);
this.Buffers = new byte[bufferSize];
} //返回指定的长度的byet[]数据,并会清理对应的储存区
public void GetAllData(out Dictionary<string, byte[]> data)
{
int DataLength = 0;
DataLength = GetAllAcceptMsg(out data);
if (DataLength > 0)
{
ClearList(DataLength);
}
}
//清理储存区已经发送的内容以及空包数据,以便下一次接收使用
private void ClearList(int DataLength)
{
this.sb.RemoveRange(0, DataLength);
}
/// <summary>
/// 判断储存区是否有接受完整的包
/// 并将完整的包存放,拆解
/// </summary>
/// <returns>单次有效数据的长度</returns>
private int GetAllAcceptMsg(out Dictionary<string, byte[]> data)
{
int DataLength=0;
int contentLength = 0;
int newlinePosition = 0;
string title = string.Empty;
string arr_msg = string.Empty;
byte[] buffer = this.sb.ToArray();
data = new Dictionary<string, byte[]>();
try
{
newlinePosition = StaticHelp.Search(buffer, Encoding.Default.GetBytes("\r\n\r\n\r\n"), false);//查找换行的位置
if (newlinePosition > 0)
{
arr_msg = Encoding.UTF8.GetString(buffer);
contentLength = Convert.ToInt32(CommonHelper.GetStrByRegex(arr_msg, "Content-Length: ", "\r\n"));
int dataLen = buffer.Length - newlinePosition;
if (dataLen >= contentLength)
{
title = arr_msg.Split(new string[] { "\r\n\r\n\r\n" }, StringSplitOptions.None)[0] + "\r\n";
byte[] postData = new byte[contentLength];
if (contentLength > 0)
Buffer.BlockCopy(buffer, newlinePosition, postData, 0, contentLength);
data.Add(title, postData);
DataLength = newlinePosition + contentLength;
}
}
else
{
DataLength = 0;
}
}
catch (Exception ex)
{
LogHelper.WriteLog(typeof(IpcServerDo), ex);
}
return DataLength;
}
}
 

C# 异步Socket的更多相关文章

  1. GJM :异步Socket [转载]

    原帖地址:http://blog.csdn.net/awinye/article/details/537264 原文作者:Awinye 目录(?)[-] 转载请原作者联系 Overview of So ...

  2. Python简易聊天工具-基于异步Socket通信

    继续学习Python中,最近看书<Python基础教程>中的虚拟茶话会项目,觉得很有意思,自己敲了一遍,受益匪浅,同时记录一下. 主要用到异步socket服务客户端和服务器模块asynco ...

  3. 项目笔记---C#异步Socket示例

    概要 在C#领域或者说.net通信领域中有着众多的解决方案,WCF,HttpRequest,WebAPI,Remoting,socket等技术.这些技术都有着自己擅长的领域,或者被合并或者仍然应用于某 ...

  4. Socket&GCDAsyncSocket(异步Socket)

    Socket ********************************************* 简单理解Socket 就是网络连接,可以实现两个点之间的数据通讯. •Socket允许使用长连 ...

  5. AndroidAsync :异步Socket,http(client+server),websocket和socket.io的Android类库

    AndroidAsync是一个用于Android应用的异步Socket,http(client+server),websocket和socket.io的类库.基于NIO,没有线程.它使用java.ni ...

  6. 可扩展多线程异步Socket服务器框架EMTASS 2.0 续

    转载自Csdn:http://blog.csdn.net/hulihui/article/details/3158613 (原创文章,转载请注明来源:http://blog.csdn.net/huli ...

  7. C# 实现的多线程异步Socket数据包接收器框架

    转载自Csdn : http://blog.csdn.net/jubao_liang/article/details/4005438 几天前在博问中看到一个C# Socket问题,就想到笔者2004年 ...

  8. C#异步Socket示例

    C#异步Socket示例 概要 在C#领域或者说.net通信领域中有着众多的解决方案,WCF,HttpRequest,WebAPI,Remoting,socket等技术.这些技术都有着自己擅长的领域, ...

  9. [Unity Socket]在Unity中如何实现异步Socket通信技术

    在刚刚开发Unity项目的过程中,需要用到即时通信功能来完成服务器与客户端自定义的数据结构封装. 现在将部分主要功能的实现代码抽取出来实现了可以异步Socket请求的技术Demo. 客户端脚本Clie ...

随机推荐

  1. C++标准库之泛型算法

    本文中算法都是指泛型算法. 基本要点: 1)算法使用迭代器进行操作. 2)不依赖容器,但容器希望使用算法,就必须提供接口. 3)通用算法永远不会执行容器操作.操作仅指:更改容器大小的操作.但,容器内部 ...

  2. 安装weblogic的步骤10.3.0.0

    一.安装前的注意事项: weblogic在安装前必须要有对应的jdk支持weblogic的运行.所以在安装之前一定要根据本机上安装的jdk来选择安装的weblgoic的什么版本.因为weblogic的 ...

  3. Android 获取图片真实宽高

    Resources res = mContext.getResources(); BitmapFactory.Options opts = new BitmapFactory.Options(); o ...

  4. 接口(三)——JAVA的多重继承

    一.接口的作用 ①.为了能够向上转型为多个基类型 ②.防止客户端程序员创建该类的对象——同抽象类 二.通过继承扩展接口 interface Monster{ void menace(); } inte ...

  5. Javascript自定义类

    JavaScript并不是严格的面向对象的语言,但是带有面向对象的一些特性,我们可以通过这些特性创建js中的自定义类. JavaScript中的类其实是function关键字包裹的一系列变量和方法. ...

  6. 正三角形的外接圆面积,nyoj-274

    正三角形的外接圆面积 时间限制:1000 ms  |  内存限制:65535 KB 难度:0 描述给你正三角形的边长,pi=3.1415926 ,求正三角形的外接圆面积.   输入 只有一组测试数据 ...

  7. Words-specialty

    1-100   101-200   community n.社区; 共同体; 社会团体; [生态] 群落 overview n.概观; 总的看法; 回顾,复习 transforming vi.改变,使 ...

  8. MVC 分页获取数据 及点选按钮

    @model PagedList<Lyxm.Entity.Suggestion>@using Webdiyer.WebControls.Mvc <div>    <ul ...

  9. 【测试环境】cywin的简单介绍

    有的时候,单位可能不会这么慷慨给你很多硬件设备供你在任何环境下面都能够工作,但我们有时候需要unix环境,这个时候cywin诞生了... 该工具非常强大,基本上能够满足您的基本需求: 1.安装cywi ...

  10. SQL Server 中索引的禁用与删除

    主题 1. 禁用索引 alter index index_name on table_name disable; 主题 2. 删除索引 drop index table_name.index_name ...