900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 使用AT指令与BC26进行socket通信

使用AT指令与BC26进行socket通信

时间:2024-02-09 01:16:56

相关推荐

使用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 255void 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 potr\n",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;elseprintf("[%s - %d] Connect Client:%d\n",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:%d\n",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 = %d\n",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 255void 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 potr\n",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,"Q\n") == 0 || strcmp(message,"Quit\n") == 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,0OK+QIOPEN: 0,0//发送长度为11字节的文本字符串>>>>>>>>>> AT+QISEND=0,11,Hello WorldAT+QISEND=0,11,Hello WorldOKSEND OK+QIURC: "recv",0 //上报URC,0表示接收到数据并缓存//发送长度为5字节的十六进制字符串 “01234”>>>>>>>>>> AT+QISENDEX=0,5,3031323334AT+QISENDEX=0,5,3031323334OKSEND OK//关闭 Socket 连接>>>>>>>>>> AT+QICLOSE=0AT+QICLOSE=0OKCLOSE OK

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

发送不定长数据

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

>>>>>>>>>> AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0OK+QIOPEN: 0,0//发送不定长数据>>>>>>>>>> AT+QISEND=0AT+QISEND=0//待发数据>>>>>>>>>>> xrl.yydsOKSEND OK+QIURC: "recv",0 // 0 上报URC 接收到数据//关闭 Scoket 连接>>>>>>>>>> AT+QICLOSE=0AT+QICLOSE=0OKCLOSE OK

数据的接收

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

//打开一个 Socket 服务>>>>>>>>>> AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0OK+QIOPEN: 0,0//返回0,0表示无误 连接成功//发送长度为11字节的字符串>>>>>>>>>> AT+QISEND=0,11,Hello WorldAT+QISEND=0,11,Hello WorldOKSEND OK+QIURC: "recv",0 // 0 上报URC,接收到数据//读取接收到的数据,最大长度为512字节,防止过长溢出>>>>>>>>>> AT+QIRD=0,512AT+QIRD=0,512+QIRD: 11//收到长度为11字节Hello World //收到长度为11字节的字符串OK//再次接收>>>>>>>>>> AT+QIRD=0,512AT+QIRD=0,512+QIRD: 0 // 0 表示接收区为空OK//关闭 Scoket 连接>>>>>>>>>> AT+QICLOSE=0AT+QICLOSE=0OKCLOSE OK

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

//打开一个 Socket 服务>>>>>>>>>> AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0AT+QIOPEN=1,0,"TCP","120.79.83.71",9999,0,0OK+QIOPEN: 0,0 //返回0,0表示无误 连接成功//发送长度为11字节的字符串>>>>>>>>>> AT+QISEND=0,11,Hello WorldAT+QISEND=0,11,Hello WorldOKSEND OK+QIURC: "recv",0 // 0 上报URC,接收到数据>>>>>>>>>> AT+QIRD=0,5 AT+QIRD=0,5 //指定接收数据的长度为5字节+QIRD: 5 //收到长度为5字节数据HelloOK//关闭 Scoket 连接>>>>>>>>>> AT+QICLOSE=0AT+QICLOSE=0OKCLOSE OK

直接访问模式

使用直接访问模式会在发送数据后上传给URC直接显示接收到的数据

可以看出,如果接收到的数据量较小的话,直接访问模式还是很方便的。

结尾

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。