基于muduo(多线程网络服务框架)在应用层实现了RTSP服务器
基于muduo(多线程网络服务框架)在应用层实现了RTSP服务器,支持传输H264视频格式文件,并在VLC上进行播放测试
rtsp://0.0.0.0:554/live
的IP地址epoll会监听三种类型的事件,一种是网络IO事件,一种是定时器事件,还有一种是自身线程唤醒事件
多线程服务器中的线程一般分为几类:
Channel
每一个Channel对象负责一个文件描述符的IO事件,在Channel对象中保存着IO事件的类型以及相应的回调函数,程序中的文件描述符一般都会和一个Channel对象关联,包括eventfd,listenfd,timefd等
EventLoop
每个线程只有一个EventLoop对象,线程运行loop函数,每次从epoll获得活跃事件,并通过Channel的回调函数进行处理
EventLoopThreadPool
线程池,可设置线程数并创建对应数量的EventLoopThread对象,可通过round-robin或hash两种策略获取某个线程使用
EventLoopThread
创建线程,包含一个EventLoop对象,线程运行loop函数
timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,读取的数据是超时的次数,所以能够被用于select/epoll的应用场景
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags); // 创建一个定时器描述符timerfd
// clockid:
// CLOCK_REALTIME 相对时间,从1970.1.1到目前的时间
// CLOCK_MONOTONIC 绝对时间,获取的时间为系统重启到现在的时间
// flags:
// TFD_NONBLOCK/TFD_CLOEXEC
// 返回值:
// timerfd(文件描述符)
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); // 启动或关闭fd指定的定时器
// fd: timerfd(文件描述符)
// flags: 0表示设置的是相对时间,1表示设置的是绝对时间
// new_value:
// 指定新的超时时间,设定new_value.it_value非零则启动定时器,否则关闭定时器,如果
// new_value.it_interval为0,则定时器只定时一次,即初始那次,否则之后每隔设定时间超时一次
// old_value:
// 不为null则返回定时器这次设置之前的超时时间
int timerfd_gettime(int fd, struct itimerspec *curr_value);
// 此函数用于获得定时器距离下次超时还剩下的时间
// 如果调用时定时器已经到期,并且该定时器处于循环模式,那么调用此函数之后定时器重新开始计时
struct itimerspec {
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
eventfd用于异步唤醒
int eventfd(unsigned int initval, int flags)
// 创建一个eventfd对象,该对象是一个内核维护的无符号的64位整型计数器。初始化为initval的值
// flags可以是以下三个标志位的OR结果:
// EFD_CLOEXEC fork子进程时不继承
// EFD_NONBLOCK 设置成非阻塞
// EFD_SEMAPHORE (2.6.30以后支持)支持semophore语义的read,简单说就值递减1
read()
// 读操作就是将counter值置0,如果是semophore就减1
write()
// 设置counter的值
// stdin, stdout, and stderr are 0, 1,and 2
// EpollPoller epoll_create1() fd = 3 - EpollPoller.cc:36
// createTimerfd createTimerfd() fd = 4 - TimerQueue.cc:35
// createEventfd createEventfd() fd = 5 - EventLoop.cc:36
// createNonblockingOrDie sockets::createNonblockingOrDie() fd = 6 - SocketUtil.cc:44
# 在目录/usr/src/下生成gtest目录存放源码
sudo apt-get install libgtest-dev
# 编译源码
cd /usr/src/gtest
sudo mkdir build
cd build
sudo cmake ..
sudo make
# 将编译生成的库拷贝到系统目录下
sudo cp libgtest*.a /usr/local/lib
在网络库的基础上,增加一个线程负责视频数据的转发
H264码流由一系列NAL单元组成,NAL单元由起始码,NALU头部和NALU载荷组成。起始码一般为"00 00 01"或"00 00 00 01",它用于标志一个NAL单元的开始,NALU头部占一个字节,后面都是NALU载荷的内容
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
载荷则规定了三种不同的结构模式:单一封包模式、 组合封包模式和分片封包模式
RTP协议的封包规则是:如果NAL单元小于最大传输单元MTU,就采用单一封包模式,否则采用分片封包模式
refer to RFC 3984
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| type | |
+-+-+-+-+-+-+-+-+ |
| |
| one or more aggregation units |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
OPTIONS rtsp://192.168.1.142:554/live RTSP/1.0
CSeq: 2
User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)
RTSP/1.0 200 OK
CSeq: 2
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY
DESCRIBE rtsp://192.168.1.142:554/live RTSP/1.0
CSeq: 3
User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)
Accept: application/sdp
RTSP/1.0 401 Unauthorized
CSeq: 3
WWW-Authenticate: Digest realm="-_-", nonce="b15ab645fd3d9d17d0905f45527e95e6"
DESCRIBE rtsp://192.168.1.142:554/live RTSP/1.0
CSeq: 4
Authorization: Digest username="admin", realm="-_-", nonce="b15ab645fd3d9d17d0905f45527e95e6", uri="rtsp://192.168.1.142:554/live", response="848666d183dd367fa613e3bd8670bf69"
User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)
Accept: application/sdp
RTSP/1.0 200 OK
CSeq: 4
Content-Length: 129
Content-Type: application/sdp
v=0
o=- 91574916875 1 IN IP4 192.168.1.142
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=control:track0
SETUP rtsp://192.168.1.142:554/live/track0 RTSP/1.0
CSeq: 5
Authorization: Digest username="admin", realm="-_-", nonce="b15ab645fd3d9d17d0905f45527e95e6", uri="rtsp://192.168.1.142:554/live", response="1d8f3068153ff01df9c5fe079160dfa4"
User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)
Transport: RTP/AVP;unicast;client_port=60780-60781
RTSP/1.0 200 OK
CSeq: 5
Transport: RTP/AVP;unicast;client_port=60780-60781;server_port=1618-1619
Session: 6880
PLAY rtsp://192.168.1.142:554/live RTSP/1.0
CSeq: 6
Authorization: Digest username="admin", realm="-_-", nonce="b15ab645fd3d9d17d0905f45527e95e6", uri="rtsp://192.168.1.142:554/live", response="575e53b58f49b5a377a9bf32e6077f0f"
User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)
Session: 6880
Range: npt=0.000-
RTSP/1.0 200 OK
CSeq: 6
Range: npt=0.000-
Session: 6880; timeout=60
摘要认证的加密方式使用MD5,同时使用nonce解决重放攻击问题:nonce是由服务器生成的一个随机数,客户端收到nonce后,与用户名,密码一起加密后发送给服务器(response字段),服务器根据用户名在数据库搜索密码后对response进行验证,由于nonce只使用1次,所以防止了重放攻击