我是靠谱客的博主 和谐小虾米,最近开发中收集的这篇文章主要介绍使用AT指令与BC26进行socket通信,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

在选购好NB开发板之后,我们就可以使用AT指令进行socket通信了,主要问题在于我们之前学习socket编程只需要一台电脑就可以进行学习,服务器和客户端都是在本机上运行,或者是两台处于同一局域网下的主机之间进行互相通信,而NB-IOT芯片信号是通过空中接口(Uu)发送到基站(eNB)信号塔(此段仅为个人理解),个人主机是在局域网环境下运行的,没有公网IP(可直接连接Internet),基站信号塔是找不到我们个人主机的,NB开发板就无法跟我们的主机进行通信,所以我们还需要准备一台具有公网IP的服务器,读者可以选择购买阿里或腾讯的服务器。

进行开发首先需要在个人主机上编写好程序,然后拷贝到咱们的服务器上面,这个过程可以参考我前面一篇文章 文章链接,配置好之后咱们就可以进行实验了

TCP/IP

BC26还专门出了一组TCP通信的AT指令手册:《Quectel_BC26&BC20_TCP(IP)_AT_V1.1_Preliminary》,之前就有TCP与UDP的socket通信的命令手册,这里我也不了解,可能是为了兼容,当然了TCP/IP也可以进行UDP通信,本章不讲解UDP。

命令介绍

TCP是一种面向连接、可靠的通信协议,TCP发送的信息对方必须接收到,如果出现差错或者丢失数据也可以进行重传,前提是对方必须知道,而UDP是一种面向无连接的协议,只管发送信息,至于对方是否接收到,就不用咱们关心了,接下来将介绍TCP/IP使用socket通信所用到的命令。

AT+QIOPEN=<contextID>,<connectID>,<service_type>,<IP_address>/<domain_name>,<remote_port><local_port>,<access_mode>,<<protocol_type>

打开一个Socket服务

  • <contextID>: 整形,上下文ID,范围:1-3,目前仅支持设置为1
  • <connectID>:整形,Socket服务索引,范围:0-4
  • <service_type>:字符串类型,Socket协议类型,"TCP"或"UDP"
  • <IP_address>:字符串类型,远程服务器的IP地址
  • domain_name>:字符串类型,远程服务器的域名地址
  • <remote_port>:远程服务器的端口
  • <local_port>:指定本地端口号,一般为0,系统会自动分配本地端口号
  • <access_mode>:整形,Socket的数据访问模式,0为缓存访问模式 1为直接访问模式
  • <protocol_type>:整形,IP协议类型 0为IPv4 1为IPv6
AT+QICLOSE=<connectID>

关闭一个Socket服务

  • <connectID>:整形,Socket服务索引,范围0-4
AT+QISTATE=<query_type>,<contextID>

查询Socket连接状态

  • <query_type>:整形,表示查询类型,根据通过<contextID>0 或 <contextID>1 来查询连接状态
  • <contextID>:整形,上下文ID,范围:1-3,目前仅支持设置为1
AT+QISEND=<connectID>,<send_length>,<data>

向服务器发送十六进制/文本字符串数据

  • <connectID>:整形,Socket服务索引,范围:0-4
  • <send_length>:待发数据长度,单位:字节
  • <data>:待发送数据
AT+QISEND=<connectID>

发送不定长数据

  • 向服务器发送数据,待响应“>” 后,输入要发送的数据,按"Ctrl + z"发送数据,按“Esc”取消发送
AT+QISEND=<connectID>,<send_length>

发送定长数据

  • 向服务器发送数据,待响应“>” 后,输入长度等于<send_length>的待发数据
AT+QISENDEX=<connectID>,<send_length>,<hex_string>

发送十六进制字符串数据

  • <connectID>:整形,Socket服务索引,范围:0-4
  • <send_length>:待发数据长度,最大字节512
  • <hex_string>:待发送的十六进制字符串数据
AT+QIRD=<connectID>,<read_length>

将接收到的数据缓存并读取

  • <connectID>:整形,Socket服务索引,范围:0-4
  • <read_length>:读取最大长度数据,最大值为512字节

其余命令暂时用不上,这里就不在一一列举,TCP/IP命令手册获取 命令手册连接 提取码:eu6h

数据访问模式

BC26模块支持两种数据访问模式

  • 缓存访问模式
  • 直接访问模式

当通过 AT+QIOPEN 打开一个 Socket 服务时,可以通过参数<access_mode>来指定数据访问模式。
当 Socket 服务成功打开,可以通过 AT+QISWTMD 改变数据访问模式。
1:在缓存访问模式下,可通过命令 AT+QISEND/AT+QISENDEX 发送数据。当接收到数据时,模块会
缓存所接收的数据,并有 URC 上报,格式为:+QIURC: "recv",<connectID>[,<current_recv_length>],模块
可以通过命令 AT+QIRD 来读取缓存数据
2:直接访问模式下,可通过命令 AT+QISEND/AT+QISENDEX 发送数据。模块新接收的数据会通过
上报 URC 格式为:+QIURC: "recv",<connectID>, <currect_recv_length><CR><LF><data>直接输出

服务器/客户端程序

程序跟之前无变化,可以接收多个连接并回射数据
服务端

#include <stdio.h> //printf
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <stdlib.h> //exit
#include <string.h> 
#include <sys/wait.h> //wait waitpid
#include <signal.h> //signal

#define MAXSIZE 255

void error_hanglde(char *message);
void process_hanglde(int sig);

int main(int argc,const char *argv[])
{
	int sockfd,cli_sock;
	ssize_t n;
	socklen_t clilen;
	char message[MAXSIZE];
	pid_t pid;
	
	struct sockaddr_in ser_addr,cli_addr;
	struct sigaction act;
	
	if(argc != 3)
	{
		fprintf(stderr,"Usage  %s ip potrn",argv[0]);
	}
	
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1)
		error_hanglde("sockfd() error!");
	
	bzero(&ser_addr,sizeof(ser_addr));			
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_addr.s_addr = inet_addr(argv[1]);  //设置IP地址,如果是阿里云,要设置成内网IP地址,而不是公网IP地址
	ser_addr.sin_port = htons(atoi(argv[2]));		//端口号
	
	if(bind(sockfd,(struct sockaddr *)&ser_addr,sizeof(ser_addr)) == -1)
		error_hanglde("bind() error!");
	
	if(listen(sockfd,10) == -1)
		error_hanglde("listen() error!");
	
	act.sa_handler = process_hanglde;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	
	sigaction(SIGCHLD,&act,0);
	
	//进行通信
	for(;;)
	{
		clilen = sizeof(cli_addr);
		cli_sock = accept(sockfd,(struct sockaddr *)&cli_addr,&clilen);
		if(cli_sock == -1)
			continue;
		else
			printf("[%s - %d] Connect Client:%dn",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),cli_sock);
			
		//创建子进程	
		pid = fork();
		if(pid == -1)
		{
			close(cli_sock);
			continue;
		}	
		if(pid == 0) //子进程执行区域
		{
			close(sockfd); //关闭监听套接字
			while((n = read(cli_sock,message,MAXSIZE)) != 0) //数据收发
			{
				write(cli_sock,message,n);
				message[n] = 0;
						
			}
			printf("[%s - %d] client disconnte:%dn",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),cli_sock);
		
			
			close(cli_sock);
			return 0;
		}
	}
	close(sockfd);
	close(cli_sock);
	
	return 0;
	
}




void process_hanglde(int sig)
{
	pid_t pid;
	int status;
	
	pid = waitpid(-1,&status,WNOHANG); 
	if(WIFEXITED(status))
	{
		printf("process recv id = %dn",pid);
	}
}	

void error_hanglde(char *message)
{
	fputs(message,stderr);
	fputs("n",stderr);
	exit(0);
}

客户端

#include <stdio.h> //printf
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <stdlib.h> //exit
#include <string.h> 

#define MAXSIZE 255



void error_hag(char *message);

int main(int argc,const char *argv[])
{
	int sockfd;
	ssize_t n;
	char message[MAXSIZE],ReadLine[MAXSIZE];
	
	if(argc != 3)
	{
		fprintf(stderr,"Usage  %s ip potrn",argv[0]);
	}
	
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1)
		error_hag("sockfd() error");

	
	struct sockaddr_in ser_addr;
	
	bzero(&ser_addr,sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_addr.s_addr = inet_addr(argv[1]);  //设置为公网IP地址
	ser_addr.sin_port = htons(atoi(argv[2]));		//对应服务器端口号
	
	if(connect(sockfd,(struct sockaddr *)&ser_addr,sizeof(ser_addr)) == -1)
		error_hag("connect() error");


	for(;;)
	{
		
		fputs("Input info(Q ~ Quit):",stdout);
		fgets(message,MAXSIZE,stdin);
		
		
		if(strcmp(message,"Qn") == 0 || strcmp(message,"Quitn") == 0)
			break;
		
		write(sockfd,message,strlen(message));	
		n = read(sockfd,ReadLine,MAXSIZE);
		ReadLine[n] = 0;
		fputs(ReadLine,stdout); 
		
	}
	
	close(sockfd);
}
void error_hag(char *message)
{
	fputs(message,stderr);
	fputs("n",stderr);
	exit(0);
}

启动程序后就可以去做实验了,这里我用的是我们老师自己做的工具:AT指令助手,个人用起来还是蛮舒服的,功能也很强大,很方便,AT助手工具获取链接 提取码:k05h

缓存访问模式

咱们开始先介绍BC26数据类型模式:缓存访问模式
在这里插入图片描述

接下来发送数据,用AT+QISENDAT+QISENDEX这两个命令

发送定长数据

在这里插入图片描述

//打开一个 Socket 服务
>>>>>>>>>>  AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0 
AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0

OK

+QIOPEN: 0,0
//发送长度为11字节的文本字符串
>>>>>>>>>>  AT+QISEND=0,11,Hello World
AT+QISEND=0,11,Hello World

OK

SEND OK

+QIURC: "recv",0 //上报URC,0表示接收到数据并缓存
//发送长度为5字节的十六进制字符串 “01234”
>>>>>>>>>>  AT+QISENDEX=0,5,3031323334
AT+QISENDEX=0,5,3031323334

OK

SEND OK
//关闭 Socket 连接
>>>>>>>>>>  AT+QICLOSE=0
AT+QICLOSE=0

OK

CLOSE OK

第一次发送的是字符串数据,服务器返回数据并上报URC,第二次发送的是进制形式,服务器接收到数据但没有上传到URC,原因是第一次接收的缓存数据未清空。

发送不定长数据

发送不定长数据需要用到AT+QISEND=0,此时服务器会响应一个“ > ”,等待用户输入数据。
在这里插入图片描述

>>>>>>>>>>  AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0
AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0

OK

+QIOPEN: 0,0
//发送不定长数据
>>>>>>>>>>  AT+QISEND=0
AT+QISEND=0
//待发数据
>

>>>>>>>>>>  xrl.yyds

OK

SEND OK

+QIURC: "recv",0 // 0 上报URC 接收到数据
//关闭 Scoket 连接
>>>>>>>>>>  AT+QICLOSE=0
AT+QICLOSE=0

OK

CLOSE OK

数据的接收

数据的接收需要使用AT+QIRD这个命令

//打开一个 Socket 服务
>>>>>>>>>>  AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0
AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0

OK

+QIOPEN: 0,0	//返回0,0表示无误 连接成功
//发送长度为11字节的字符串
>>>>>>>>>>  AT+QISEND=0,11,Hello World
AT+QISEND=0,11,Hello World

OK

SEND OK

+QIURC: "recv",0 // 0 上报URC,接收到数据
//读取接收到的数据,最大长度为512字节,防止过长溢出
>>>>>>>>>>  AT+QIRD=0,512
AT+QIRD=0,512

+QIRD: 11	//收到长度为11字节
Hello World //收到长度为11字节的字符串

OK
//再次接收
>>>>>>>>>>  AT+QIRD=0,512
AT+QIRD=0,512

+QIRD: 0 // 0 表示接收区为空

OK
//关闭 Scoket 连接
>>>>>>>>>>  AT+QICLOSE=0
AT+QICLOSE=0

OK

CLOSE OK

其次也可以指定接收的长度

//打开一个 Socket 服务
>>>>>>>>>>  AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0
AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0

OK

+QIOPEN: 0,0 //返回0,0表示无误 连接成功
//发送长度为11字节的字符串
>>>>>>>>>>  AT+QISEND=0,11,Hello World
AT+QISEND=0,11,Hello World

OK

SEND OK

+QIURC: "recv",0 // 0 上报URC,接收到数据

>>>>>>>>>>  AT+QIRD=0,5 
AT+QIRD=0,5 //指定接收数据的长度为5字节

+QIRD: 5 //收到长度为5字节数据
Hello

OK
//关闭 Scoket 连接
>>>>>>>>>>  AT+QICLOSE=0
AT+QICLOSE=0

OK

CLOSE OK

直接访问模式

使用直接访问模式会在发送数据后上传给URC直接显示接收到的数据
在这里插入图片描述
可以看出,如果接收到的数据量较小的话,直接访问模式还是很方便的。

结尾

文章如有误望指正,也希望本文章能帮助读者,临近毕业,对口相关岗位还是很难找,不知道后面会不会坚持下来从事IT行业

最后

以上就是和谐小虾米为你收集整理的使用AT指令与BC26进行socket通信的全部内容,希望文章能够帮你解决使用AT指令与BC26进行socket通信所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部