20131202
- 继续实现
tcp_server
和tcp_connection
类 - 解决namespace冲突
- 将公用函数移到
toolkit.[h|cpp]
中
20131203
tcp_server
可以监听端口了- 用telnet连本机生成新连接时,
handle_accept()
成功调用
20131204
- 解决telnet连接后直接断开的问题。
- error code显示125,可能是
tcp_connection
类或者存放数据的buffer在callback函数被调用时,已经被析构了。 - 解决方法有两种,一是手动new/delete;二是让
tcp_connection
类继承enable_shared_from_this
接口,在需要this指针的callback函数中,用shared_from_this()
生成一个指向当前对象的shared_ptr
,保证在有引用的情况下,对象始终存在。第二种在boost例子和网上讨论中比较常用。 - 目前用第二种方法解决了该问题。
- error code显示125,可能是
- 消除编译时的warning。
- 实现server从
socket
读取数据,直到读出0x03
时,调用callback函数处理读到的包。 - buffer中的数据可能不止到
0x03
的位置,手动提取数据包自身的部分到一个char*
。
遇到的问题如下:
- 提取到
char*
(或者char[]
)就意味着要在结构体内把它delete掉(或者任它自己在结构体执行完毕后被回收),这样就无法将处理数据包的逻辑写到另一个函数去,除非传参的时候不传指针或者引用,而传拷贝。每个包都复制很多遍的话,时间长了会产生很多内存碎片。- 需要找一个方式,用
shared_ptr
来传递参数给数据包处理函数,让对象在没有引用之后自动析构。
- 需要找一个方式,用
20131205
- 用
shared_ptr
封装packet对象再传递给处理函数,处理完毕后对象会自动被回收 - 解决读到
0x00
之后的字符被砍掉的问题。下面有描述。 - 解码报文内容,提取报文头、报文类型、数据、校验和以及报文尾。
- 验证校验和
遇到的问题如下:
- 昨天用
boost::asio::async_read_until()
实现读到某个特定字符后调用callback,用telnet实验的时候因为无法输入0x00
,0x02
,0x03
等值,用$
,&
等符号来试的。但是读到一个char[]
之后,因为char string的定义,0x00
被认为是字符串结束符。后面的数据被抛弃了。现在用unsigned char[]
和memcpy
来操作buffer中读到的数据,可以应对包含0x00
的内容了。
20131206
- 配置基于Vagrant和VirtualBox的虚拟机编译环境。
- 只要一个命令
vagrant up
,就可以在Windows/Mac/Linux下自动下载、配置虚拟机(Ubuntu 12.04 LTS) - 虚拟机包含项目依赖的所有工具和库,可以直接编译运行SkyEye-Server项目代码。
- 只要一个命令
- 使用虚拟编译环境的文档已经更新至 http://202.11.11.201/skyeye/skyeye-server/blob/master/README.md
- 增加对不同版本gcc的支持
- Ubuntu 12.04装的gcc版本是4.6,而Fedora 19则是4.7。前者需要
CXX_FLAGS
中用-std=c++0x
,后者则是-std=c++11
。 - 在CMakeLists.txt中用判断语句针对不同的gcc版本分别设定
CXX_FLAGS
- Ubuntu 12.04装的gcc版本是4.6,而Fedora 19则是4.7。前者需要
- 解决不同版本boost存在区别的问题
- 此前只在boost 1.53和1.55版本下编译过项目,Ubuntu 12.04的boost版本是1.46.1,还没有
boost::container::map
。修改项目代码,用std::map
替代了。
- 此前只在boost 1.53和1.55版本下编译过项目,Ubuntu 12.04的boost版本是1.46.1,还没有
20131207
- 向孙亚清请教通信程序的实现,包括收发包的方式、数据的格式等
- 与孙亚清讨论数据库的定义
20131209
- 更新数据库描述文档 http://202.11.11.201/skyeye/skyeye-server/blob/master/doc/db_spec.asciidoc
- 写数据转义、解转义函数
- 写DTU通信逻辑
- 登录/回复
- 收发keepalive报文
- 收发查询报文
- 发送转发报文
20131210
- 写
packet_device
class及相关enum、工具函数 - 写设备通信报文的接收/发送逻辑
- 检查代码中的参数,能传(const)引用的就不传值
- 划分
namespace skyeye::net
和namespace skyeye::db
,尽量避免常量名冲突或是超长的常量名 - 补全已有代码的注释
- 更新文档
20131211
- 取消toolkit.h中定义的全部变量和函数的static声明,改成extern声明,在toolkit.cpp中定义一次,消除了unused警告。
- 给
tcp_server
和tcp_connection
增加database_operator
类型的handle以调用数据库相关的函数。 - 写设备状态报文的解析函数。
- 将遍历STL容器的for loop改写为 range based for loop
- 用Google Logging Library写日志文件
- 编译安装了glog
- FindGlog.cmake让CMake自动找到glog安装位置
- CMake在build时会生成
bin\log
目录(bin
是make install
的安装位置)
- toolkit中写了从byte array生成hex string的函数,用来打印收到和发送报文的raw data
遇到的问题如下:
- 事件报文格式未定义
- 通信协议中,读取状态、事件、播放列表时,如果设备返回的
result != 0x00
,也就是读取失败的情况下,监控中心PC行为未定义。
20131212
- 从
grps.c
确定状态、事件报文格式 - 修改
constants.h
定义的变量名,尽量同gprs.h相似 - 写状态、事件报文分割函数,用给定字符分行及分列
- 完成
skyeye_server
,tcp_server
,tcp_connection
的调用逻辑 - 读DTU、设备的操作超时会记录在log了。
- 实现设备返回
result != 0x00
后的3次重试及失败记录。
20131213
- 更新Vagrant配置,让虚拟机安装glog。
- 完成考核表
- 更新数据库event表字段定义,更新status格式注释
- dotfiles & dotfiles-private on GitHub
- Build vim74 for Fedora
20131214
- 学习串口编程
20131216
- 补全数据库定义 http://202.11.11.201/skyeye/skyeye-server/blob/master/doc/db_spec.asciidoc
- 写生成table的SQL语句 http://202.11.11.201/skyeye/skyeye-server/blob/master/doc/init_db.sql
- 补全文档中对服务端各个功能对应的网络和数据库操作描述
- 写
tcp_connection
成员函数内的数据库操作
20131217
- 写
tcp_connection
成员函数内收到DTU报文的数据库操作- 收
0x00
登录- 更新
device.device_state_id
- 更新
device.last_connected
- 更新
- 收
0x01
心跳- 更新
device.last_connected
- 更新
- 收
- 写
tcp_connection
成员函数内收到设备报文的数据库操作- 收
0x00
读取状态- 解码状态 "type,attr,value,error\n"
- 插入
status
全部新状态 - 根据status_warn表计算device_state TODO:读数据库生成本地status_warn
- 更新
device.last_connected
- 更新
device.last_status
- 更新
device.device_state_id
- 删除
status
中的旧状态
- 更新
- 收
0x01
读取事件- 更新
device.last_connected
- 更新
device.last_event
- 更新
event
- 更新
- 收
0x02
读取播放列表- 更新
device.last_connected
- 更新
device.last_playlist
- 更新
playlist
- 更新
- 收
20131218
sms_modem
类通过串口连接modem- 数据库读到要求连接任务时,发送"手机号#CONN"格式的数据请求设备连接
遇到的问题如下:
Q: make之后的可执行文件正常,make install复制的可执行文件运行时提示"Symbol lookup error"
A: 用 ldd
查看依赖关系发现两个文件依赖的glog不同,一个在 /usr/lib64
,另一个在 /usr/local/lib
,后者是我编译安装的位置。用我编译的版本覆盖 /usr/lib64
中的版本,问题解决。
20131219
(在202.11.11.162上)
- 安装Virutalbox+Vagrant
- 安装虚拟机编译运行skyeye_server
- 解决VM启动、glog安装、gcc版本等问题
- 修改配置绑定9800端口
遇到的问题如下:
Q: 202.11.11.162上虚拟机无法启动
A: 系统是32位的,运行不了64位虚拟机,修改配置使用precise32,成功
Q: 安装glog库报错"configure: error: cannot run /bin/bash ./config.sub"
A: 安装libtool解决
Q: 自带gcc是4.6的,不支持c++11;ppa中最新是4.8.1的,不支持map的initialization list
A: 从源码编译安装gcc-4.8.2(正在解决)
20131220
- 重新打包虚拟机
- 以ubuntu cloud镜像为起点安装项目用的gcc、cmake、boost、libmysqldev等依赖,目前的机器性能和网速要花几小时
- 昨晚到今天上午把配置好的虚拟机重新打包成一个vagrant box,使用这个box只要一两分钟就能重新部署一台新的服务器了
- 代码中增加了一些退出条件,比如连不上数据库或者数据库没有需要的表,则提示错误后退出。
- 202.11.12.186上
skyeye_db
数据库建表 - 202.11.11.162开放9801端口进行监听
- 将"测试3"设备的DTU连接信息改为202.11.11.162:9801
- 在数据库中添加设备: "测试3"
- 成功连接设备
- 修正连接设备过程中查到的代码逻辑错误
遇到的问题如下:
主要都是配置虚拟机时遇到的问题。
Q: Ubuntu 12.04的工具太旧了,编译安装新版的gcc、cmake等需要安装许多依赖,昨天用一台机器跑了大半天还没结束
A: 换成13.10版了。这样可以直接用 apt-get
安装编译好的二进制文件,快很多。
Q: 编译时和运行时的library include path不一致
A: 人工建立symlink再运行 ldconfig
解决
Q: 运行时提示glibc版本不对
A: 编译安装gcc之后,安装脚本并未把编译出来的 libstdc++6.so
复制到应有的路径,人工复制并建立必要的symlink解决
20131221
- 修改配置文件,设置虚拟机时区为
Asia/Shanghai
- 修改glog库,以支持自定义日志文件扩展名;重新编译、安装glog库
- 清理虚拟机
/
,/boot
,/EMPTY
,将打包的box文件从700MB降至435MB
20131223
- 写测试用客户端,自动往数据库提交任务
20131224
- 实现设备登录后立即读取状态和事件
- 更新数据库定义
- keepalive标识 类型为
tinyint(1)
0表示false,1表示true - 增加读DTU状态用户命令类型
command.id=7
- keepalive标识 类型为
- 增加查询DTU状态功能
- 收到DTU状报文态后,解析IP为char[]、端口、版本号为unsigned short、信号质量为unsigned char
- 数据库的keepalive标识和tcp_server自己管理的map同步
- 用go语言写测试客户端,向数据库插入一些任务
遇到的问题如下:
- DTU登录报文的地理信息4字节格式不明
- DTU状态报文的定位数据9字节格式不明
20131225
- 确定DTU状态表的定义,写相应的SQL语句
- 将DTU状态查询的数据库更新逻辑补全
- 写手动测试的sql脚本
- 修正一个sql语句错误写法
- 修正一个计算checksum的bug
20131226
- 修正服务端处理格式不正确的状态和事件报文时会访问到空指针的问题
- 收到
0x20
分割的数据,代码用0x2C
分割不出4个字段:客户端更新前,先用0x20
分割。 - 用
0x20
分割,遇到日期会有5个字段,SQL执行报错:将错误写到log,跳过该行。
- 收到
- 修正服务端
escape()
的bug - 用Go语言写模拟客户端(用于调试和N客户端性能测试),已实现
- 发起和断开TCP连接
- 构建DTU Login报文并发送
- 收到服务器发回的包并显示
- 对收到的包类型进行switch/case处理
20131227
- 继续实现模拟客户端的功能
- 产生状态,收到读状态报文时,发送状态
- 写SkyeyeClient struct,改写函数为对象方法
- 在goroutine中执行client行为
- 并发连接测试
- 补全服务端函数注释并重新生成文档
遇到的问题如下:
- 模拟客户端同时发起很多连接时,会被服务器拒绝连接
- 服务器收到同时过多连接时,连到数据库的socket会失效
20131228
- 继续实现模拟客户端的功能
- 应答DTU Keepalive
- 产生事件,收到读事件报文时,发送事件
- 产生一个字符串作为播放列表,收到读播放列表时,发送该字符串
- 模拟客户端的如下功能配合本地或202.11.111.162上的服务器通信测试成功
- DTU登陆、Keepalive
- 读状态、事件、播放列表
遇到的问题如下
Q: 模拟客户端同时发起很多连接时,会被服务器拒绝连接
A: 让模拟客户端做完DTU登陆后就退出,这种情况下大约突发100多连接时,server报segment fault,这个需要跟踪一下哪个指针出了问题
Q: 服务器连到数据库的socket会失效
A: 昨天测试的时候,每次并发连接数多了会这样。今天发现只要server开着的时候,用mysql workbench或者navicat去连接数据库并提交任务,server也会报错说lost connection。可能是配置问题,需要查查文档。
20131230
- 调试服务端mysql连接问题
- 数据库连接丢失后自动重连
- 多线程执行server connection handler
遇到的问题如下
Q: Lost connection to MySQL server during query
A: /var/log/mysqld.log
中记录了
131230 9:31:04 [Warning] IP address '202.11.12.186' could not be resolved: Name or service not known
原因是mysqld会通过dns检查用户的hostname是否属于用户表里指定的域。一旦dns查询超时或者失败,就会报这个错,然后这个连接就被abort了。
解决方法是在 /etc/my.cnf
中加入一行
skip-host-resolve
Q: 解决了上一个问题之后,并发query多的时候,还是会丢失连接
A: /var/log/mysqld.log
中记录了
131230 10:08:21 [Warning] Aborted connection 3 to db: 'skyeye_db' user: 'skyeye_admin' host: '202.11.12.186' (Unknown error)
并且这个问题的发生还有个条件,就是两条query“同时”由同一个user在同一个host上执行,就会丢失连接。如果让测试程序并行生成两个客户端连接,立刻DTU login,每次都会出问题;两次生成客户端之间sleep几十毫秒,则极少出问题。
目前查不到是什么原因导致的。现在通过 mysql_options()
函数让服务端的mysql query发生lost connection错误后自动重连重试避开这个问题。
20131231
- 用Valgrind检查内存泄露,现在服务端正常退出的话,全部内存都释放了。
- 找到数据库访问产出segfault的原因
- 测试服务端多连接性能 (虚拟客户端连接服务端,并发送DTU Login报文,收Login反馈)
- 并发1000个客户端,服务端可以处理全部连接
- 并发2000个客户端,服务端处理不了会reject传入连接,之前的连接处理完之后,再传入连接正常接受
- 目前只有socket读写是async的,handler全部是blocking的方法。当线程在执行handler的时候,就无法接收新连接了,需要等到handler完成,进入下一次async读或写的时候,才会调度到接收连接的方法。虽然目前的测试条件下,还是可以处理几百到几千个并发连接,但如果某个时刻,数据库访问突然慢了,那么handler就卡在数据库操作上,这段时间就都无法接收新连接。
遇到的问题如下:
Q: 并发连接很多时,在执行 mysql_query()
的位置会报segfault
A: 问题出在多线程访问 MYSQL*
指针上,执行 mysql_query()
不是thread-safe的。
现在先改成单线程执行服务端。
多线程跑服务端还不能简单的用多个线程执行所有组件,得把非线程安全的部分提出来单独在一个线程执行。
目前想到可能的多线程解决方法
exec_sql()
加锁- 用boost strand令数据库操作无法并行
- 把
database_operator
放在单独的io_service
执行。
考虑到上面提到的性能问题,多线程是必须的,未来几天会尝试这些方法,解决数据库访问。