BC26 支持使用 Socket 进行 TCP 和 UDP 协议通信,这两个协议也是 BC26 支持的众多通信协议的基础。本文讲解如何使用这两个协议与服务器端进行通信。在学习这篇文章前,请首先使用AT+CPSMS=0指令将节电模式(PSM)关闭。否则每隔十来秒,MCU 就进入休眠状态,让你不得不重启评估板,相当扰人,学习期间,评估板一直插在 USB 口上,供电无忧,无所谓节电模式。进行 Socket 通信的所有 AT 指令都可以在 AT 指令助手的指令集合面板中通过选择【BC26 Socket 命令】项获取,并查看手册。

命令介绍

我们知道,TCP 是面向连接的协议,TCP 发送信息必须确保对方能够收到,即使对方无法收到信息本方也可以知晓。而 UDP 是面向无连接的协议,只管将信息发送给对方,至于对方能否收到,本方就不关心了。接下来首先介绍本文在使用 Socket 通信时所使用到的命令。

AT+QSOC=<domain>,<type>,<protocol>

创建一个 TCP 或 UDP Socket。

  • <domain>:表示使用的是 IPv4 还是 IPv6,其中 1 表示 IPv4。
  • <type>:表示协议类型,其中 1 表示 TCP;2 表示 UDP。
  • <protocol>:表示协议类型,其中 1 表示 IP;2 表示 ICMP。

例如:AT+QSOC=1,1,1表示创建一个使用 IPv4 的,使用 TCP/IP 的 Socket。

AT+QSOCON=<socket_id>,<remote_port>,<remote_address>

使用 Socket 进行远程连接。

  • <socket_id>:BC26 一共支持同时使用 5 个 Socket 进行通信,编辑为 0~4,此参数指定其中一个 Socket。
  • <remote_port>:通信端口,0~65535。
  • <remote_address>:远端 IP 地址。

例如:AT+QSOCON=0,5000,"193.112.19.116"表示将 0 号 Socket 向地址为 193.112.19.116 的远端服务器的 5000 端口发起连接。

AT+QSOSEND=<socket_id>,<data_len>,<data>

向远端发送数据。

  • <socket_id>:Socket 的编号,范围 0~4。
  • <data_len>:数据的长度,以字节为单位。对于 ASCII 码来说,一个字符的长度为 1。
  • <data>:发送的数据,使用 16 进制数字表示。记住,无论你发送的是整数、浮点数、字符串,还是其他的数据类型,这里统统要以字节的 16 进行数字表示。假设你要发送一个字符H,查ASCII表,找到H的编码为 0x48,则发送 48 即可。如果要发送的是Hello,则发送内容为:48656C6C6F

例如:AT+QSOSEND=0,5,48656C6C6F表示让 0 号 Socket 发送 5 个字节长度的数据[0x48,0x65,0x6C,0x6C,0x6F]。

+QSONMI=<socket_id>,<data_len>

此为非请求结果码(URC),即服务器端主动发送过来的数据,而非客户端请求的数据。表示收到服务器端发来的数据。

  • <socket_id>:表示是第几号 Socket 收到的数据。
  • <data_len>:表示收到的数据的长度。

例如:+QSONMI=0,5表示 0 号 Socket 收到远端发送过来的 5 个字节的数据。

+QSORF=<socket_id>,<req_length>

从 Socket 接收数据。此命令配合上一条命令使用。

  • <socket_id>:指示接收数据的 Socket 编号。
  • <req_length>:接收多长的数据。

例如:AT+QSORF=0,5表示从 0 号 Socket 的接收数据缓冲中读取 5 个字节的数据。

AT+QSODIS=<socket_id>

断开 Socket 连接。

  • <socket_id>指示要断开的 Socket 的编号。

AT+QSOCL=<socket_id>

关闭 Socket。

  • <socket_id>指示要断开的 Socket 的编号。

NB-IOT 的使用场景,必定是每日少量数据的传送,所以 Socket 打开后,传完数据就应当立即断开连接并关闭。

使用 TCP 协议进行通信

本节演示如何使用 AT 指令跟远程服务器进行 TCP 通信,条件是必须要有一台具有公网 IP 的服务器,没有的话,无法进行实验,不过问题也不大。后面主要还是使用更高层级的协议跟电信、华为、阿里的专用物联网云通信的。

服务器端

服务器端,具体开发环境的搭建请参考上一篇文章

新建一个 TCPSocket 文件夹,进入后,使用命令dotnet new console创建一个新控制台项目,代码如下:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Text; namespace TCPSocket
{
class Program
{
static void Main(string[] args)
{ //设置服务器 IP,如果是腾讯云,必须使用内网地址,而不是公网 IP。
IPAddress ip = IPAddress.Parse("172.16.0.11");
IPEndPoint point = new IPEndPoint(ip, 5000); //端口指定为 5000
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
s.Bind(point);
s.Listen(5);
Console.WriteLine("服务器开始侦听...");
Socket subSocket = s.Accept(); //等待新连接,本程序仅能接受一个客户端的连接
Console.WriteLine("获取一个来自{0}的连接", subSocket.RemoteEndPoint.ToString());
//创建线程接收客户端的消息
Task.Factory.StartNew(() => ReceiveMessage(subSocket), TaskCreationOptions.LongRunning);
//发送消息
while (true)
{
string sendStr = Console.ReadLine();
if(sendStr=="") return; //如果在控制台不输入任何字符直接按回车,则退出程序
byte[] sendBuff = Encoding.ASCII.GetBytes(sendStr);
subSocket.Send(sendBuff, sendBuff.Length, SocketFlags.None);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
s.Close();
}
} //监听客户端连接的线程方法
static void ReceiveMessage(Socket subSocket)
{
byte[] buff = new byte[1024]; //创建一个接收缓冲区
try
{
while (true)
{
int count = subSocket.Receive(buff, buff.Length, SocketFlags.None);
//下面这个判断是非常必要的,否则有可能导致不停地接收到长度为 0 的数据,导致 CPU 占用率100%
if (count == 0)
{
subSocket.Close();
return;
}
//将接收到的数据转化为 ASCII 字符
string recvStr = Encoding.ASCII.GetString(buff, 0, count);
Console.WriteLine($"接收到数据:{recvStr}");
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
subSocket.Close();//客户端关闭时会引发异常,此时关闭此连接
Console.WriteLine("客户端已退出连接。");
}
}
}
}

本程序仅用于测试 AT 指令,所以写得比较简单,实现如下功能:

  1. 仅可以接收一个连接,如果需要再次接收,请重新运行程序。当程序接收到一个新的连接时,会打印客户端 IP 地址。
  2. 当收到消息时,会将消息转化为 ASCII 码字符串打印。
  3. 在控制台输入字符按回车,可将字符串转化为字节数组发给客户端,注意,与客户端连接后方可实现此功能。
  4. 不输入任何内容按回车即可退出程序。

运行程序

  • 服务器端使用dotnet run运行程序启动服务,显示“服务器开始侦听...”。
  • 打开AT指令助手,载入【TCP Socket】脚本。
  • 发送指令:AT+QSOC=1,1,1,创建 Socket。
  • 发送指令:AT+QSOCON=0,5000,"193.112.19.116",连接服务器,注意端口和 IP 地址请自行更改。
  • 发送指令:AT+QSOSEND=0,5,48656C6C6F,发送数据“Hello”,观察服务器是否收到。
  • 发送指令:AT+QSOSEND=0,10,54435020536F636B6574,发送数据“TCP Socket”,观察服务器是否收到。
  • 服务器端发送数据abc
  • 客户端收到+QSONMI=0,3
  • 发送指令:AT+QSORF=0,3接收缓冲区数据。
  • 服务器端发送数据good-bye
  • 客户端收到+QSONMI=0,8
  • 发送指令:AT+QSORF=0,8接收缓冲区数据。
  • 发送指令:AT+QSODIS=0断开连接。
  • 发送指令:AT+QSOCL=0关闭 Socket。

运行效果如下图所示。

使用 UDP 协议进行通信

UDP 协议具有资源消耗小,处理速度快的优点,但它是不靠的。接下来演示使用 UDP 协议进行通信,使用 UDP 和使用 TCP 的思维方式是不一样的。UDP 没有连接,也就没有所谓的断开连接,但有意思的是,使用 AT 指令发送 UDP 信息时,依然和 TCP 一样,需要进行连接和断开连接操作(在 C# 中写 UDP 程序是没有这些的)。你不能说建立一个连接后,在这个连接的基础上你来我往。UDP 的一个 Socket 只会侦听某一端口的所有信息,而这个信息可能是不同客户端发送的,所以,每次接收信息都要创建一个新的IPEndPoint。所以这次我把程序改为将接收到的信息原样发回。

服务器端

新建一个 UDPSocket 文件夹,进入后,使用命令dotnet new console创建一个新控制台项目,代码如下:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Text; namespace TCPSocket
{
class Program
{
static void Main(string[] args)
{ //设置服务器 IP,如果是腾讯云,必须使用内网地址,而不是公网 IP。
IPAddress ip = IPAddress.Parse("172.16.0.11");
IPEndPoint point = new IPEndPoint(ip, 5000); //端口指定为 5000
Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); udpSocket.Bind(point);
Console.WriteLine("服务器开始侦听...");
//创建线程接收客户端的消息
Task.Factory.StartNew(() => ReceiveMessage(udpSocket), TaskCreationOptions.LongRunning);
Console.ReadLine(); //按回车直接退出程序
} //监听客户端连接的线程方法
static void ReceiveMessage(Socket udpSocket)
{
byte[] buff = new byte[1024]; //创建一个接收缓冲区
try
{
while (true)
{
EndPoint remote = new IPEndPoint(IPAddress.Any, 0);
int count = udpSocket.ReceiveFrom(buff, ref remote);
//将接收到的数据转化为 ASCII 字符
string recvStr = Encoding.ASCII.GetString(buff, 0, count);
Console.WriteLine($"接收到来自{remote.ToString()}数据:{recvStr}");
udpSocket.SendTo(buff, 0, count, 0, remote);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
udpSocket.Close();
}
}
}
}

本程序实现如下功能:

  1. 由于没有所谓的连接,可以一直的接收 UDP 信息,也就是说,客户端可以多次创建 Socket 向服务器发信息,而服务器不需要重启程序便可接收所有 Socket 的信息。
  2. 当收到消息时,会将消息转化为 ASCII 码字符串打印。
  3. 服务器在收到信息后,原样返回给客户端。
  4. 按回车即可退出程序。

程序运行效果如下图所示,由于和上一个程序类似,我不再详细讲解。

感想

做完两个程序后,还是有一些感想的,我买了两块板,有一块信号不太好,导致调试程序的过程异常痛苦,使用另一块板之后才能确定是信号而不是程序的问题,将来万物互联,NB-IOT 的连接设备数量会非常巨大,在这种情况下,使用 TCP 协议或许并不是最优选择,毕竟 TCP 光建立一个连接就要费不少周折,而且保持连接还会耗费服务器资源和带宽,NB-IOT 的带宽并不优裕。UDP 是更好的选择,来信息直接处理,无需耗费资源保持连接,不占用带宽,可靠性问题可以通过应用层的控制来满足。所以我们也看到,BC26 上的大多数协议也是基于 UDP 进行开发的。这些协议我在后面会一一讲解。

使用 AT 指令进行 Socket 通信的更多相关文章

  1. TCP Socket通信详细过程

    下面这篇文章是参考"骏马金龙"博客中 不可不知的socket和TCP连接过程 https://www.cnblogs.com/f-ck-need-u/p/7623252.html ...

  2. python - socket通信笔记

    参考: 通过编写聊天程序来熟悉python中多线程和socket的用法:https://www.cnblogs.com/mingjiatang/p/4905395.html python socket ...

  3. 我看不下去鸟。。。。Java和C#的socket通信真的简单吗?

    这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...

  4. php简单实现socket通信

    socket通信的原理在这里就不说了,它的用途还是比较广泛的,我们可以使用socket来做一个API接口出来,也可以使用socket来实现两个程序之间的通信,我们来研究一下在php里面如何实现sock ...

  5. Socket通信类

    package com.imooc; import java.io.BufferedReader; import java.io.IOException; import java.io.InputSt ...

  6. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-介绍

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  7. socket通信

    socket通信 一:socket基于Tcp连接,数据传输有保证 二:socket连接的建立过程: 1:服务器监听 2:客户端发出请求 3:建立连接 4:通信 三:一个简单的例子:服务器端每隔一段时间 ...

  8. Android之Socket通信、List加载更多、Spinner下拉列表

    Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务 ...

  9. .NET开源高性能Socket通信中间件Helios介绍及演示

    一:Helios是什么 Helios是一套高性能的Socket通信中间件,使用C#编写.Helios的开发受到Netty的启发,使用非阻塞的事件驱动模型架构来实现高并发高吞吐量.Helios为我们大大 ...

随机推荐

  1. C++模板相关知识点总结

    1:在 C++ 中,模板是泛型编程的基础.模板是创建类或函数的蓝图或公式. 2:模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以 ...

  2. 亚洲唯一,阿里云SLB位列Gartner全球网络负载均衡市场前五

    近日,Gartner发布了最新的全球企业级网络设备市场份额报告“Market Share: Enterprise Network Equipment by Market Segment, Worldw ...

  3. mysql 中合并查询结果union用法 or、in与union all 的查询效率

    mysql 中合并查询结果union用法 or.in与union all 的查询效率 (2016-05-09 11:18:23) 转载▼ 标签: mysql union or in 分类: mysql ...

  4. MongoDB 基础API使用

    1. 基本概念: 1.1. MongoDB 保留数据库名: admin: 从权限的角度来看,这是"root"数据库.要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限 ...

  5. P2P需集齐四大证照

    今后做P2P需集齐四大证照 比牌照制还严 2016-09-05 11:53:24 分类:热点观察 作者:汪祖刚 8月24日,P2P网贷监管细则在千呼万唤中始出来,整个行业内外的关注热度可谓史无前例.有 ...

  6. Mac OS X 常用快捷键一览

  7. Android 字体库的使用

    开发Android的人大多都知道,Android里面对字体的支持少得可怜,默认情况下,TextView  的 typeface 属性支持 "Sans","serif&qu ...

  8. 6 获取请求头和URL信息

    @app.route("/req",methods=['GET','POST'])def req(): print(request.headers) #请求头的信息全部在这里面 p ...

  9. 支付宝防并发方案之"一锁二判三更新"

    每年支付宝在双11和双12的活动中,都展示了绝佳的技术能力.这个能力不但体现在处理高TPS量的访问,更体现在几乎不会出错,不会出现重复支付的情况,那这个是怎么做到的呢? 诚然,为了实现在高并发下仍不会 ...

  10. 第二次 C++作业

    1.为什么要用函数? 函数是相对独立的,经常使用的功能抽象化表现形式,函数的优势在于,编写之后可以被重复使用,使用时可以只关心函数的功能和使用方法而不必关心函数的具体实现,这样可以有利于代码重用,可以 ...