Socket异步通信及心跳包同时响应逻辑分析。
有段时间没有更博了,刚好最近在做Socket通信的项目,原理大致内容:【二维码-(加logo)】-->提供主机地址和端口号信息(直接使用【ThoughtWorks.QRCode.dll】比较简单就不赘述了,核心方法直接贴出来)。然后使用手机APP扫描进行连接服务器,然后通过TCP/IP协议进行握手传输,接收到的图片按照一定的规则进行排列。实时使用心跳包进行检测,服务器进行实时响应。
一、二维码+logo核心方法:
引用的命名空间是:using ThoughtWorks.QRCode.Codec;随便用一个控件(比如:Image就可以show出来!)
/// <summary>
/// 初始化二维码对象并根据传入的信息进行创建
/// </summary>
public Bitmap Inilized_QRCode(string Imformation)
{
encoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;//编码方式(注意:BYTE能支持中文,ALPHA_NUMERIC扫描出来的都是数字)
encoder.QRCodeScale = ;//大小(值越大生成的二维码图片像素越高)
encoder.QRCodeVersion = ;//版本(注意:设置为0主要是防止编码的字符串太长时发生错误)
encoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;//错误效验、错误更正(有4个等级)
Bitmap bp = encoder.Encode(Imformation, Encoding.GetEncoding("GB2312"));//进行位图编码
Image image = bp;
return bp;
}
二维码+logo
二、重点分析心跳包与握手协议:
本次采用的是Socket进行异步传输,首先要定义服务器地址和端口号(区分网路上其他主机的唯一标识);开始之前先申明:本文采用的机制是一个客户端只使用一个Socket,服务器通过端口进行监听,并发响应客户端。
* 服务器
A、定义Socket 和 获取 监听服务器本机地址 端口号
//监听初始化
private Socket listener = null;//连接客户端
private Socket RemoteClient = null;//接收消息 private int port = ;
private IPEndPoint listenEP = null;
B、定义好之后开始初始化数据 ,使用AcceptCallback 进行消息回调并等待客户端触发接收数据
private void RunsSocketServer()
{
listener = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
listener.Bind(listenEP);
listener.Listen();
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
null); LB_TXT.Content = "监听已开启" + listener.LocalEndPoint.ToString() + ""; }
C、回调函数
private void AcceptCallback(IAsyncResult iar)
{
try
{
RemoteClient = listener.EndAccept(iar);
//同进程上的线程异步
this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
{
LB_TXT.Content = string.Format("{0}已连接到服务器", RemoteClient.RemoteEndPoint.ToString());
}); ReceivePicHandShakes();//握手协议及接收图片 }
catch (Exception err)
{
this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
{
LB_TXT.Content = err.Message;
});
return;
}
}
D、通过开辟一个新的线程进行异步接收数据(图中的协议使用XXX代替,请大家根据自己的协议需要进行定义)
Thread _timers = null;
private Socket _SocketClient = null;
byte[] RecvBytes = new byte[];
private int Numimgs = ;//记录接收成功的张数
//byte[] ReImgBytes = new byte[8];//接收图片的头字节
public event ReceivedBitmapHandler ReceivedBitmap;
private delegate void RestartThread();//定义委托 public TransferHandler(Socket client)
{
_SocketClient = client;
} #region 服务器端
public void BeginReceive()
{
//采用线程直接接收的方式
_timers = new Thread(StartToReceive);
_timers.IsBackground = true;
_timers.Start();
}
/// <summary>
/// 开始接收消息
/// </summary>
private void StartToReceive()
{
//由于long占8位字节,所以先获取前8位字节数据
IAsyncResult iar = _SocketClient.BeginReceive(
RecvBytes,
,
RecvBytes.Length,
SocketFlags.None,
null,
null);
int len = _SocketClient.EndReceive(iar);
string ReceivemMsg = Encoding.ASCII.GetString(RecvBytes, , len);
if (ReceivemMsg.IndexOf("xxx") > )//区分业务消息和心跳检测消息
{
_SocketClient.Send(Encoding.ASCII.GetBytes("xxx"));//回应心跳包
RecvBytes = new byte[RecvBytes.Length]; //清空数据
StartToReceive();//回应心跳包完成之后继续等待接收
}
else if (ReceivemMsg == "xxx")//如果收到这个请求,告诉客户端可以开始发送第一张图片了
{
string order = "xxx";
byte[] orderdata = Encoding.ASCII.GetBytes(order); //把字符串编码为字节
_SocketClient.Send(orderdata);
RecvBytes = new byte[]; //开始接收图片
StartToReceive();//回应指令后完成之后继续等待接收
}
else if (ReceivemMsg == "xxx")
{
//停止接收图片
string order = "xxxx";//通知客户端已经完成接收这次所有图片,结束传输;
byte[] orderdata = Encoding.ASCII.GetBytes(order); //把字符串编码为字节
_SocketClient.Send(orderdata);
StartToReceive();
}
else
{
int offset = ;
int length = BitConverter.ToInt32(RecvBytes, offset); //先获取文件长度
ReceiveFile(length);
} } public void ReceiveFile(long filelen)
{
MemoryStream ms = new MemoryStream();
int bytesRead = ;
long count = ;
byte[] buffer = new byte[]; while (count != filelen)
{
try
{
bytesRead = _SocketClient.Receive(buffer);
ms.Write(buffer, , bytesRead);
count += bytesRead;
}
catch (Exception ex)
{ }
}
ReceivedBitmap(new Bitmap(ms)); //接收完成之后清空数据,继续接收
buffer = new byte[buffer.Length];//缓存
RecvBytes = new byte[RecvBytes.Length];//用于接收 string order = "xxxx";//通知客户端已经收到图片,请继续
byte[] orderdata = Encoding.ASCII.GetBytes(order); //把字符串编码为字节
_SocketClient.Send(orderdata);
Numimgs++; if (Numimgs >= )
{
RecvBytes = new byte[];
Numimgs = ;
}
StartToReceive(); //接收完成之后继续接收
}
#endregion
* 客户端
A、心跳包发送(代码中XXX解释同上)
#region 心跳检测
private void HeartBeatsTests()
{
Thread sendEcho = new Thread(new ThreadStart(socketSend));
sendEcho.Start();
sendEcho.IsBackground = true;
} public void socketSend()
{
while (true)
{
Thread.Sleep();//每十秒发一次,响应发3次
//备注:IsSendingImgs必须放在sleep底下,防止等待的空余时间发送导致服务端继续接收心跳包
if (!IsSendingImgs)//如果是任务还在执行不允许第二次发送,必须要等待完成
{
try
{
this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
{
this.richTextBox.AppendText("开始发送心跳包..." + Environment.NewLine);
});
client.Send(Encoding.ASCII.GetBytes("xxxx"));//发送心跳暗号
}
catch (SocketException)
{
Thread.CurrentThread.Abort();
//throw ex;
}
//标记发送的次数
this.MyLostTime++;
//如果外发了3次请求暗号后仍不见服务器的回应,则认为客户端已经与服务器断开联系了
if (this.MyLostTime >= )
{
//IsSendingImgs = true;
TimeSpan t = DateTime.Now - lastConnect;
if (t.TotalSeconds > )
{
this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
{
//this.richTextBox.cont
this.richTextBox.AppendText("与服务器失去联系..." + Environment.NewLine);
});
}
else
{ this.countnum++;
this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
{
//this.richTextBox.cont
this.richTextBox.AppendText("服务器响应第" + countnum.ToString() + "次,响应时间是:" + DateTime.Now.ToString() + ";" + Environment.NewLine);
});
}
//每次确定后重置
this.MyLostTime = ;
}
}
else
{
lastConnect = DateTime.Now;//每十秒刷新,防止图片传送任务完成之后导致时间错误;
}
} } #endregion
B、响应机制(发送图片及接收消息触发停止、开启心跳包代码中XXX解释同上)
/// <summary>
/// 协议请求
/// </summary>
public void SendSocketData()
{
string sendStr = "xxx"; //向服务器发送请求:发送3张图片的指令
byte[] buffer = Encoding.ASCII.GetBytes(sendStr); //把字符串编码为字节
_SendQuest.Send(buffer); //发送
_SendQuest.BeginReceive(Rbuffer, , Rbuffer.Length, , new AsyncCallback(CallbackRuquest), null);//等待接收并回调
} /// <summary>
/// 回调告诉socket可以执行下一步了
/// </summary>
/// <param name="iar"></param>
private void CallbackRuquest(IAsyncResult arr)
{
int aa = _SendQuest.EndReceive(arr);
string Rc_Msg = Encoding.ASCII.GetString(Rbuffer, , aa);
if (Rc_Msg == "xxx")
{
khd.IsSendingImgs = false;//启动心跳包--用于接收完成之后继续开启心跳包用
Numimgs = ;
_SendQuest.BeginReceive(Rbuffer, , Rbuffer.Length, , new AsyncCallback(CallbackRuquest), null);//等待接收并回调
}
else
{
//结束接收
if (Numimgs >= )
{
//结束
byte[] buffer = Encoding.ASCII.GetBytes("xxx");
_SendQuest.Send(buffer);
_SendQuest.BeginReceive(buffer, , buffer.Length, , new AsyncCallback(CallbackRuquest), null);//等待接收并回调 }
else
{
//接收成功并进行回调 if (Rc_Msg.IndexOf("xxx") > )
{
Socket_Client.lastConnect = DateTime.Now;//重新赋值
khd.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
{
khd.richTextBox.AppendText("接收到服务器的响应消息" + Environment.NewLine);
});
_SendQuest.BeginReceive(Rbuffer, , Rbuffer.Length, , new AsyncCallback(CallbackRuquest), null);//等待接收并回调
}
else if (Rc_Msg == "xxx" || Rc_Msg == "xxx")
{
FileInfo fi = new FileInfo(Filename);//获取文件
byte[] len = BitConverter.GetBytes(fi.Length);//文件数据
//首先把文件长度发送过去
_SendQuest.BeginSendFile(Filename, len, null, TransmitFileOptions.UseDefaultWorkerThread, new AsyncCallback(EndSendback), null);
_SendQuest.BeginReceive(Rbuffer, , Rbuffer.Length, , new AsyncCallback(CallbackRuquest), null);//等待接收并回调
//清空数据
len = new byte[fi.Length];
Numimgs++;
}
}
}
} /// <summary>
/// 结束发送
/// </summary>
/// <param name="eof"></param>
private void EndSendback(IAsyncResult eof)
{
_SendQuest.EndSendFile(eof);
} /// <summary>
/// 结束接收
/// </summary>
/// <param name="eof"></param>
private void EndCallback(IAsyncResult eof)
{
//结束接收
_SendQuest.EndReceive(eof); }
这边只着重做出如何响应如何发送的逻辑实现,做个笔记。还是蛮有意思的。以下是Demo效果图:

此文的Demo下载地址:Socket心跳通信
Socket异步通信及心跳包同时响应逻辑分析。的更多相关文章
- Socket异步通信及心跳包同时响应逻辑分析(最后附Demo)。
		
有段时间没有更博了,刚好最近在做Socket通信的项目,原理大致内容:[二维码-(加logo)]-->提供主机地址和端口号信息(直接使用[ThoughtWorks.QRCode.dll]比较简单 ...
 - java  Socket 长连接 心跳包 客户端 信息收发 demo
		
今天写了个socket的测试小程序,代码如下 import java.io.IOException; import java.io.InputStream; import java.io.Output ...
 - web socket 心跳包的实现方案
		
web socket 心跳包的实现方案05/30/2010 现在网络环境错综复杂,socket心跳包是获得健康强壮的连接的有效解决方案,今天,我们就在web socket中实现心跳包方案,是的,尽管我 ...
 - Socket心跳包机制总结【转】
		
转自:https://blog.csdn.net/qq_23167527/article/details/54290726 跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器, ...
 - C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包
		
原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...
 - socket的心跳包机制
		
网络中的接收和发送数据都是使用操作系统中的SOCKET进行实现.但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题.可是如何判断这个套接字是否还可以使用呢?这个就需要在系统中创建心跳机 ...
 - socket 心跳包机制
		
心跳包的发送,通常有两种技术 方法1:应用层自己实现的心跳包 由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动 ...
 - loadrunner使用socket协议来实现客户端对服务器产生压力实例。(通过发送心跳包,达到连接多个客户端的目的)
		
#include "lrs.h" vuser_init(){ char *ip; int handler; //编写获取LR分配的Vuser IP函数,将IP保存在ip变量中. i ...
 - 【Socket】关于socket长连接的心跳包
		
TCP的socket本身就是长连接的,那么为什么还要心跳包呢? 在smack里有个30s发送一个空消息的线程,同样关于心跳包(keepalive) 据网络搜索到的资料解释如下 内网机器如果不主动向外发 ...
 
随机推荐
- VR全景:电商巨头的角逐
			
VR全景智慧城市:京东推"京东梦"挑战淘宝Buy+ ,VR购物谁主沉浮? VR全景智慧城市是国内首家商业全景平台,结合先进VR虚拟现实技术,以线下实体为依托,将空间还原到线上,用户 ...
 - vuex所有核心概念完整解析State Getters  Mutations  Actions
			
vuex是解决vue组件和组件件相互通信而存在的,vue理解起来稍微复杂但一旦看懂择即为好用 安装: npm install --save vuex 引入 import Vuex from 'vuex ...
 - css如何让div和页面等高?
			
我们都知道,只要是block状态的标签,宽度和父级等宽,或者设置宽度100%也可以等宽,但设置高度100%是不管用的,那么如何让标签和页面等高呢,除了用js去动态计算设置高度值,用css也可以 只要将 ...
 - lucene全文搜索之二:创建索引器(创建IKAnalyzer分词器和索引目录管理)基于lucene5.5.3
			
前言: lucene全文搜索之一中讲解了lucene开发搜索服务的基本结构,本章将会讲解如何创建索引器.管理索引目录和中文分词器的使用. 包括标准分词器,IKAnalyzer分词器以及两种索引目录的创 ...
 - cocoapod升级
			
1.0 重新安装问题 cd /user/xx/.cocoapod/repos rm -rf master pod setup /user/xx/.cocoapod/repos 查看目录文件夹大小: d ...
 - iOS  bug调试技巧学习----breakpoint&condition
			
给断点添加条件 - (void)testCondition2 { NSArray *array = @[@"我们", @"一起", @"来" ...
 - xfire调用webservice接口的实现方式
			
package com.test; import java.net.URL; import org.codehaus.xfire.client.Client; import org.codehaus. ...
 - [1] C# IS & AS讲解
			
c# 中 is和as 操作符是用来进行强制类型转换的 is : 检查一个对象是否兼容于其他指定的类型,并返回一个Bool值,永远不会抛出异常 object o = new object(); if ( ...
 - 深入理解JavaScript中的闭包
			
闭包没有想象的那么简单 闭包的概念在JavaScript中占据了十分重要的地位,有不少开发者分不清匿名函数和闭包的概念,把它们混为一谈,我希望借这篇文章能够让大家对闭包有一个清晰的认识. 大家都知道变 ...
 - ViewPager实现无限轮播踩坑记
			
最近笔者想通过ViewPager来实现一个广告Banner,并实现无限轮播的效果,但是在这个过程中踩了不少的坑,听我慢慢道来.如果大家有遇到和我一样的情况,可以参考我的解决方法,没有那就更好,如果针对 ...