操作系统:CentOS
开发板:fl2440
开发模块:A7(GPS/GPRS),RT3070(无线网卡)
****************************************************************************************************************************************************************************************
前言:本博文实现的功能是:fl2440开发板运行客户端程序,将GPS的定位信息通过串口读取出来,然后将定位信息发送到服务器上。
(当然服务器上跑的是自己编写的服务器端程序)这个就有点类似共享单车上面装的GPS定位系统,然后公司就可以根据其共享单车的地理位置信息进行定位管理。不过我这个只是一个小程序,功能比较单一,只是初步学习网络socket编程,不敢妄加定论,如有不对的地方,谢请指正。
对于初次学习网络socket编程的人来说,了解epoll的原理是必要的,网上有大量文章介绍epoll的原理以及它的用法,所以本文不做过多的赘述,只是简单的分析。
1.什么是epoll?
我的理解是:epoll是linux网络编程多路复用中的一种新的事件触发机制,相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率,不过实现的原理大致相同,都是通过监听客户端套接字fd,也就是如果有多个客户端程序连接服务器,然后服务器端就将监听到的套接字fd存放在一个集合里,如果发现客户端套接字fd发生可读,可写,以及错误事件时,服务器端就进行相应的处理。不过epoll与poll及select不同的是,epoll采用的是基于事件的就绪通知方式。
在select/poll中,进程只有在调用一定的函数后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
epoll实现一共就三个函数:
(1). int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
(2).int epoll_ctl(int epfd, int op, int fd, struct
epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事.
struct epoll_event结构如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll
events */
epoll_data_t data; /* User
data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge
Triggered)模式,这是相对于水平触发(Level
Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里.
(3).int epoll_wait(int epfd, struct
epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个
maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
2.如何使用epoll?
通过在包含一个头文件#include <sys/epoll.h> 以及几个简单的API将可以大大的提高你的网络服务器的支持人数。
首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。
这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。
之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event
events, int max
events, int timeout)来查询所有的网络接口,看哪一个可以读
哪一个可以写了。基本的语法为:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写
件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件
围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则返回。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主
辑在同一个线程的话,则可以用0来保证主循环的效率。epoll_wait返回之后应该是一个循环,遍利所有的事件。
epoll简单分析之后,进入正题:
(3).硬件连线我使用的是两根USB转串口线,一根连接开发板,通过串口方式来连接开发板,一根连接GPS模块,当然也可以通过ssh代理远程登陆开发板,不过开发板能够与PC通信(两种方法:1.网线有线连接使其开发板与PC相连。2.插上无线网卡,开发板能上网,无线连接开发板)
- /*********************************************************************************
- * Copyright: (C) 2017 zoulei
- * All rights reserved.
- *
- * Filename: client.c
- * Description: This file
- *
- * Version: 1.0.0(2017年06月21日)
- * Author: zoulei <zoulei121@gmail.com>
- * ChangeLog: 1, Release initial version on "2017年06月21日 19时17分40秒"
- *
- ********************************************************************************/
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <netinet/in.h>
- #include <errno.h>
- #include <stdlib.h>
- #include "gps.h"
- #define GPS_LEN 1024
- #define PORT 9997
- int set_serial(int fd,int nSpeed, int nBits, char nEvent, int nStop);
- int main (int argc, char **argv)
- {
- int fd=0;
- int n=0;
- int i=0;
- int sockfd;
- int rec_len;
- GPRMC gprmc;
- char sendbuf[1024] ;
- char buff[GPS_LEN];
- char *str=NULL;
- char *dev_name="/dev/ttyUSB0";
- struct sockaddr_in servaddr;
- /*打开"/dev/ttyUSB0"设备*/
- if((fd=open(dev_name,O_RDWR|O_NOCTTY|O_NDELAY))<0)
- {
- perror("Can't Open the ttyUSB0 Serial Port");
- return -1;
- }
- set_serial( fd,9600,8,'N',1);//串口配置函数
- /* 判断命令端输入的参数是否正确 */
- if( argc != 2)
- {
- printf("usage: ./client <ipaddress>\n");
- exit(0);
- }
- /* 创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
- if(( sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
- {
- perror("socket");
- exit(0);
- }
- /* 初始化 */
- memset(&servaddr,0,sizeof(servaddr)); /* 数据初始化-清零 */
- servaddr.sin_family = AF_INET; /* 设置IPv4通信 */
- servaddr.sin_port = htons(PORT);/* 设置服务器端口号 */
- /* IP地址转换函数,将点分十进制转换为二进制 */
- if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
- {
- printf("inet_pton error for %s\n",argv[1]);
- exit(0);
- }
- /* 将套接字绑定到服务器的网络地址上*/
- if( connect( sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0)
- {
- perror("connected failed");
- exit(0);
- }
- while(1)
- {
- sleep(2);
- /*读串口设备获取GPS定位信息*/
- if((n=read(fd,buff,sizeof(buff)))<0)
- {
- perror("read error");
- return -1;
- }
- /*将GPS定位信息发送到服务器端*/
- if(send(sockfd,buff,strlen(buff),0)< 0 )
- {
- printf("send the gps datas error:%s(errno: %d)\n", strerror(errno), errno);
- exit(0);
- }
- printf("read buff:%s\n",buff);
- }
- close(sockfd);
- close(fd);
- return 0;
- }
- CC=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
- objs=uart1.o client.o
- srcs=uart1.c client.c
- client_test: $(objs)
- $(CC) -o client_test $(objs)
- @make clean
- client.o: $(srcs) gps.h
- $(CC) -c $(srcs)
- uart1.o: uart1.c
- $(CC) -c uart1.c
- clean:
- rm *.o
make编译之后生成client_test可执行文件,然后将其烧录到开发板。赋予可执行,可读,可写权限。
- /*********************************************************************************
- * Copyright: (C) 2017 zoulei.
- * All rights reserved.
- *
- * Filename: sev.c
- * Description: This file
- *
- * Version: 1.0.0(06/22/2017)
- * Author: zoulei <zoulei121@gmail.com>
- * ChangeLog: 1, Release initial version on "06/22/2017 11:25:16 AM"
- *
- ********************************************************************************/
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/epoll.h>
- #include <errno.h>
- #define OPEN_MAX 100
- typedef unsigned int UINT;
- typedef int BYTE;
- typedef struct __gprmc__
- {
- UINT time;/* gps定位时间 */
- char pos_state;/*gps状态位*/
- float latitude;/*纬度 */
- float longitude;/* 经度 */
- float speed; /* 速度 */
- float direction;/*航向 */
- UINT date; /*日期 */
- float declination; /* 磁偏角 */
- char dd;
- char mode;/* GPS模式位 */
- }GPRMC;
- int gps_analyse (char *buff,GPRMC *gps_data)
- {
- char *ptr=NULL;
- if(gps_data==NULL)
- {
- return -1;
- }
- if(strlen(buff)<10)
- {
- return -1;
- }
- if(NULL==(ptr=strstr(buff,"$GPRMC")))
- {
- return -1;
- }
- sscanf(ptr,"$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));
- return 0;
- }
- int print_gps (GPRMC *gps_data)
- {
- printf(" \n");
- printf(" \n");
- printf("===========================================================\n");
- printf("== 全球GPS定位导航模块 ==\n");
- printf("== Author:zoulei ==\n");
- printf("== Email:zoulei121@gmail.com ==\n");
- printf("== Platform:fl2440 ==\n");
- printf("===========================================================\n");
- printf(" \n");
- printf("===========================================================\n");
- printf("== GPS state bit : %c [A:有效状态 V:无效状态] \n",gps_data->pos_state);
- printf("== GPS mode bit : %c [A:自主定位 D:差分定位] \n", gps_data->mode);
- printf("== Date : 20%02d-%02d-%02d \n",gps_data->date%100,(gps_data->date%10000)/100,gps_data->date/10000);
- printf("== Time : %02d:%02d:%02d \n",(gps_data->time/10000+8)%24,(gps_data->time%10000)/100,gps_data->time%100);
- printf("== 纬度 : 北纬:%d度%d分%d秒 \n", ((int)gps_data->latitude) / 100, (int)(gps_data->latitude - ((int)gps_data->latitude / 100 * 100)), (int)(((gps_data->latitude - ((int)gps_data->latitude / 100 * 100)) - ((int)gps_data->latitude - ((int)gps_data->latitude / 100 * 100))) * 60.0));
- printf("== 经度 : 东经:%d度%d分%d秒 \n", ((int)gps_data->longitude) / 100, (int)(gps_data->longitude - ((int)gps_data->longitude / 100 * 100)), (int)(((gps_data->longitude - ((int)gps_data->longitude / 100 * 100)) - ((int)gps_data->longitude - ((int)gps_data->longitude / 100 * 100))) * 60.0));
- printf("== 速度 : %.3f m/s \n",gps_data->speed);
- printf("== \n");
- printf("============================================================\n");
- return 0;
- }
- int main(int argc, char *argv[])
- { int max = 0 ;
- int i = 0 ;
- int len = 0 ;
- int sockfd ;
- int epfd ;
- int connfd ;
- int ret ;
- int fd[OPEN_MAX];
- char buff[512];
- GPRMC gprmc;
- struct epoll_event event; // 告诉内核要监听什么事件
- struct epoll_event wait_event; //内核监听完的结果
- struct sockaddr_in server_addr;
- /* AF_INET 表示采用TCP/IP协议族 SOCK_STREAM 表示采用TCP协议 */
- if(( sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)
- {
- perror("creat socket error");
- return -1;
- }
- memset(&server_addr,0,sizeof(server_addr));
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(9997);
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- /* 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程) */
- if(( bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)))<0)
- {
- perror("bind error");
- return -1;
- }
- /* 将socket设置为监听模式,10表示等待连接队列的最大长度 */
- if( listen(sockfd, 10) < 0)
- {
- perror("listen error");
- return -1;
- }
- memset(fd,-1, sizeof(fd));
- fd[0] = sockfd;
- epfd = epoll_create(10); // 创建一个 epoll 的句柄,参数要大于 0, 不然没有太大意义
- if( -1 == epfd )
- {
- perror ("epoll_create error");
- return -1;
- }
- event.data.fd = sockfd; //监听套接字
- event.events = EPOLLIN; // 表示对应的文件描述符可以读
- /*事件注册函数,将监听套接字描述符 sockfd 加入监听事件 */
- if(( ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event)) == -1)
- {
- perror("epoll_ctl");
- return -1;
- }
- while(1)
- {
- /* 监视并等待多个文件描述符的属性变化(是否可读)
- 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时.*/
- ret = epoll_wait(epfd, &wait_event, max+1, -1);
- /*监测sockfd(监听套接字)是否存在连接 */
- if(( sockfd == wait_event.data.fd ) && ( EPOLLIN == wait_event.events & EPOLLIN ))
- {
- struct sockaddr_in cli_addr;
- int clilen = sizeof(cli_addr);
- /* 从tcp完成连接中提取客户端*/
- if(( connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen)) < 0)
- {
- perror("accept faild");
- return -1;
- }
- /* 将提取到的connfd放入fd数组中,以便下面轮询客户端套接字 */
- for(i=1; i<OPEN_MAX; i++)
- {
- if(fd[i] < 0)
- {
- fd[i] = connfd;
- event.data.fd = connfd; //监听套接字
- event.events = EPOLLIN; // 表示对应的文件描述符可以读
- /* 事件注册函数,将监听套接字描述符 connfd 加入监听事件 */
- ret = epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &event);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
- break;
- }
- }
- /* max更新 */
- if(i > max)
- max = i;
- /* 如果没有就绪的描述符,就继续epoll监测,否则继续向下看*/
- if(--ret <= 0)
- continue;
- }
- for(i=1; i<=max; i++)
- {
- if(fd[i] < 0)
- continue;
- if(( fd[i] == wait_event.data.fd ) && ( EPOLLIN == wait_event.events & (EPOLLIN|EPOLLERR) ))
- {
- /*接受客户端数据 */
- if((len = recv(fd[i], buff, sizeof(buff), 0)) < 0)
- {
- if(errno == ECONNRESET)//tcp连接超时、RST
- {
- close(fd[i]);
- fd[i] = -1;
- }
- else
- perror("read error:");
- }
- else if(len == 0)//客户端关闭连接
- {
- close(fd[i]);
- fd[i] = -1;
- }
- else //正常接收到客户端的数据
- buff[len]='\0';
- printf("receive the data:%s \n",buff);
- memset(&gprmc, 0 , sizeof(gprmc));
- gps_analyse(buff,&gprmc);
- print_gps(&gprmc);
- /*所有的就绪描述符处理完了,就退出当前的for循环,继续epoll监测 */
- if(--ret <= 0)
- break;
- }
- }
- }
- close(sockfd);
- close(epfd);
- return 0;
- }
测试结果:
linux下编程epoll实现将GPS定位信息上报到服务器的更多相关文章
-
Linux下编程学习一
本篇主要记录一些在学习LINUX下编程时,, C和C++语言的一些基础的常识, 一. 函数指针 void MyFun(int x); 函数声明 void (*FunP)(int ); 函数指针声明 下 ...
-
Linux下 高性能、易用、免费的ASP.NET服务器
Linux下 高性能.易用.免费的ASP.NET服务器 http://www.jexus.org/#
-
linux下保存下位机输出的串口信息为文件
linux下保存下位机输出的串口信息为文件 1.stty -F /dev/ttyUSB0 raw (转换成raw模式) 2.stty -F /dev/ttyUSB0 speed 115200 (设置波 ...
-
在linux下有没有什么软件可以连接windows上的MSSQL SERVER
在linux下有没有什么软件可以连接windows上的MSSQL SERVER GUI的http://dbeaver.jkiss.org/ http://bbs.csdn.net/topics/391 ...
-
Linux下如何通过命令检查网卡是否插上网线
How To:Linux下如何通过命令检查网卡是否插上网线 主要工具为ethtool来检查,主要关注的字段为"Link detected",注意如下的输出,其中em4实际物理上 ...
-
Linux下使用fstatfs/statfs查询系统相关信息
Linux下使用fstatfs/statfs查询系统相关信息 1. 功能 #include < sys/statfs.h > int statfs(const char *path, ...
-
Linux 下编程
关于Linux 下的C语言编译命令和编程要点! https://www.cnblogs.com/wfwenchao/p/3985153.html?utm_source=tuicool&utm_ ...
-
Linux下编程获取本地IP地址的常见方法
转载于:http://blog.csdn.net/k346k346/article/details/48231933 在进行linux网络编程时,经常用到本机IP地址.本文罗列一下常见方法,以备不时之 ...
-
linux 下如何查看和踢除正在登陆的其它用户 ==>;Linux下用于查看系统当前登录用户信息的4种方法
在linux系统中用pkill命令踢出在线登录用户 由于linux服务器允许多用户登录,公司很多人知道密码,工作造成一定的障碍 所以需要有时踢出指定的用户 1/#who 查出当前有那些终端登录(用 ...
随机推荐
-
vs2015 企业版 专业版 密钥
亲测可用 专业版:HMGNV-WCYXV-X7G9W-YCX63-B98R2企业版:HM6NR-QXX7C-DFW2Y-8B82K-WTYJV
-
eclipse 查看变量或方法在什么地方被调用的快捷键
选中方法名,点鼠标右键,菜单里有个”打开调用层次结构 ( Open Call Hierarchy )“,选中或者按下快捷键Ctrl+Alt+H,就在下面栏目里能看到调用的树形结构了. 或者: 1.双击 ...
-
apache高负载性能调优
先阅读apache配置优化建议如下,再对相关参数进行调整,观察服务器状况.Apache配置优化建议:进入/usr/local/apache2/conf/extra 目录下Apache优化,经过上述操作 ...
-
h5新增标签兼容性
<address> 标签定义文档或文章的作者/拥有者的联系信息. 兼容所有浏览器 <area> 标签定义图像映射中的区域(注:图像映射指得是带有可点击区域的图像).兼容所有浏 ...
-
Android开发之旅:android架构
本篇将站在*的高度——架构,来看android.我开篇就说了,这个系列适合0基础的人且我也是从0开始按照这个步骤来 学的,谈架构是不是有点螳臂挡车,自不量力呢?我觉得其实不然,如果一开始就对整个an ...
-
docker install for centos7
CentOS Docker runs on CentOS 7.X. An installation on other binary compatible EL7 distributions such ...
-
AutoTile 自动拼接(一) 学习与实践
恩,大家好,这两天江苏冷空气袭击,下了今年 第一场第二场雪. 不过今天我要说的 ,和 上面的 屁关系都没有. 今天要说的是 2d无缝自动拼接.大家有没有玩过 RPG Maker VX Ace. 类似 ...
-
Asp.Net Core基于JWT认证的数据接口网关Demo
近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...
-
资深程序员整理出来的Python面试题
转载链接:https://www.cnblogs.com/fcxwz/p/9225791.html
-
spring boot集成ehcache 2.x 用于hibernate二级缓存
https://www.jianshu.com/p/87b2c309b776 本文将介绍如何在spring boot中集成ehcache作为hibernate的二级缓存.各个框架版本如下 spring ...