Rtsp服务器搭建(RTSP协议实现)
RTSP协议
OPTIONS rtsp://127.0.0.1:8554/live RTSP/1.0\r\n
CSeq: 2\r\n
\r\n
DESCRIBE rtsp://127.0.0.1:8554/live RTSP/1.0\r\n
CSeq: 3\r\n
Accept: application/sdp\r\n
\r\n
SETUP rtsp://127.0.0.1:8554/live/track0 RTSP/1.0\r\n
CSeq: 4\r\n
Transport: RTP/AVP;unicast;client_port=54492-54493\r\n
\r\n
PLAY rtsp://127.0.0.1:8554/live RTSP/1.0\r\n
CSeq: 5\r\n
Session: 66334873\r\n
Range: npt=0.000-\r\n
\r\n
头文件(函数实现)
导入库
//"main.h"
#include<iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
extern "C"
{
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
}
#define SERVER_PORT 8554
#define SERVER_RTP_PORT 55532
#define SERVER_RTCP_PORT 55533
#define BUF_MAX_SIZE (1024*1024)
创建tcpsocket套接字(createTcpSocket)
static int createTcpSocket()
{
int sockfd;
int on = 1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
return -1;
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
return sockfd;
}
创建udpsocket套接字(createUdpSocket)
static int createUdpSocket()
{
int sockfd;
int on = 1;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
return -1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
return sockfd;
}
绑定地址和端口(bindSockerAddr()
//绑定地址和端口
static int bindSockerAddr(int sockfd, const char*ip, int port)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.S_un.S_addr = inet_addr(ip);
if (bind(sockfd, (sockaddr*)&addr, sizeof(sockaddr)) < 0)
return -1;
return 0;
}
接收客户端连接请求(acceptClient)
static int acceptClient(int sockfd, string *ip, int *port)
{
int clientfd;
int len = 0;
sockaddr_in addr;
//将addr初始化为0
memset(&addr, 0, sizeof(addr));
len = sizeof(addr);
clientfd = accept(sockfd, (sockaddr*)&addr, &len);
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.2.160");
if (clientfd < 0)
return -1;
*ip = inet_ntoa(addr.sin_addr);
*port = ntohs(addr.sin_port);
return clientfd;
}
获取报文第一行数据(getlinefrombuf)
static char *getlinefrombuf(char*buf, char*line)
{
while (*buf != '\n')
{
*line = *buf;
line++;
buf++;
}
*line = '\n';
line++;
*line = '\0';
++buf;
return buf;
}
处理OPTIONS方法
static int handleCmd_OPTIONS(char* result, int cseq)
{
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
"\r\n",
cseq);
return 0;
}
处理DESCRIBE方法
static int handleCmd_DESCRIBE(char* result, int cseq, char* url)
{
char sdp[500];
char localIp[100];
sscanf_s(url, "rtsp://%[^:]:", localIp, sizeof(localIp));
sprintf_s(sdp, 256, "v=0\r\n"
"o=- 9%ld 1 IN IP4 %s\r\n"
"t=0 0\r\n"
"a=control:*\r\n"
"m=video 0 RTP/AVP 96\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=control:track0\r\n",
time(NULL), localIp);
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\nCSeq: %d\r\n"
"Content-Base: %s\r\n"
"Content-type: application/sdp\r\n"
"Content-length: %d\r\n\r\n"
"%s",
cseq,
url,
strlen(sdp),
sdp);
return 0;
}
处理SETUP方法
static int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n"
"Session: 66334873\r\n"
"\r\n",
cseq,
clientRtpPort,
clientRtpPort + 1,
SERVER_RTP_PORT,
SERVER_RTCP_PORT);
return 0;
}
处理PLAY方法
static int handleCmd_PLAY(char* result, int cseq)
{
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Range: npt=0.000-\r\n"
"Session: 66334873; timeout=60\r\n\r\n",
cseq);
return 0;
}
消息处理函数
static void doClient(int clientSockfd, string clientIP, int clientPort,
int serverRtpSockfd, int serverRtcpSockfd)
{
char method[40];//方法
char url[100];//url
char version[40];//版本号
int cseq;//序列号
int clientRtpPort;//rtp端口号
int clientRtcpPort;//rtcp端口号
char *bufPtr;
char *rbuf = new char[BUF_MAX_SIZE];
char *sbuf = new char[BUF_MAX_SIZE];
char line[100];
while (true)
{
int recvlen;
//接收客户端请求
recvlen = recv(clientSockfd, rbuf, BUF_MAX_SIZE, 0);
if (recvlen <= 0)
goto out;
rbuf[recvlen] = '\0';//结尾加'\0'结束符
//打印报文
cout << "----------------------C->S----------------------\n" << endl;
cout << rbuf;
bufPtr = getlinefrombuf(rbuf, line);
//解析方法
if (sscanf_s(line, "%s %s %s\r\n", method, sizeof(method), url, sizeof(url), version, sizeof(version)) != 3)
{
cerr << "解析错误!!!" << endl;
goto out;
}
//解析序列号
bufPtr = getlinefrombuf(bufPtr, line);
if (sscanf_s(line, "CSeq: %d\r\n", &cseq) != 1)
{
cerr << "解析错误!!!" << endl;
goto out;
}
//如果是setup则再解析client_port
if (!strcmp(method, "SETUP"))
{
while (1)
{
bufPtr = getlinefrombuf(bufPtr, line);
if (!strncmp(line, "Transport:", strlen("Transport:")))
{
sscanf_s(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", &clientRtpPort, &clientRtcpPort);
break;
}
}
}
if (!strcmp(method, "OPTIONS"))
{
if (handleCmd_OPTIONS(sbuf, cseq))
{
cerr << "无法处理OPTIONS" << endl;
goto out;
}
}
else if (!strcmp(method, "DESCRIBE"))
{
if (handleCmd_DESCRIBE(sbuf, cseq, url))
{
cerr << "无法处理DESCRIBE" << endl;
goto out;
}
}
else if (!strcmp(method, "SETUP"))
{
if (handleCmd_SETUP(sbuf, cseq, clientRtpPort))
{
cerr << "无法处理SETUP" << endl;
goto out;
}
}
else if (!strcmp(method, "PLAY"))
{
if (handleCmd_PLAY(sbuf, cseq))
{
cerr << "无法处理PLAY" << endl;
goto out;
}
}
else
{
goto out;
}
cout << "----------------------S->C----------------------\n" << endl;
cout << sbuf;
send(clientSockfd, sbuf, strlen(sbuf), 0);
}
out:
delete[]rbuf;
delete[]sbuf;
closesocket(clientSockfd);
}
主函数
初始化库
WORD version = MAKEWORD(2, 2);//初始化套接字
WSADATA wsa;//初始化套接字
int is = WSAStartup(version, &wsa);
if (is != 0)
{
cerr << "初始化失败!!!" << endl;
WSACleanup();
return -1;
}
版本校验
if (2 != HIBYTE(wsa.wVersion) || 2 != LOBYTE(wsa.wVersion))
{
//版本不匹配
WSACleanup();
return -1;
}
创建套接字
int serverSockfd;
int serverRtpSockfd, serverRtcpSockfd;
int ret;
创建tcp套接字
serverSockfd = createTcpSocket();
if (serverSockfd < 0)
{
cerr << "TCP套接字创建失败!!!" << endl;
return -1;
}
绑定地址和端口
ret = bindSockerAddr(serverSockfd, "0.0.0.0", SERVER_PORT);
if (ret < 0)
{
cerr << "绑定地址或端口失败!!!" << endl;
return -1;
}
监听网络端口
ret = listen(serverSockfd, 10);
if (ret < 0)
{
cerr << "监听端口失败!!!" << endl;
return -1;
}
创建rtp/rtcp udp套接字
//创建rtp udp套接字
serverRtpSockfd = createUdpSocket();
//创建rtcp udp套接字
serverRtcpSockfd = createUdpSocket();
if (serverRtcpSockfd < 0 || serverRtpSockfd < 0)
{
cerr << "udp创建失败!!!" << endl;
return -1;
}
if (bindSockerAddr(serverRtpSockfd, "0.0.0.0", SERVER_RTP_PORT) < 0 ||
bindSockerAddr(serverRtcpSockfd, "0.0.0.0", SERVER_RTCP_PORT) < 0)
{
cerr << "UDP套接字创建失败!!!" << endl;
return -1;
}
cout << "rtsp://127.0.0.1:" << SERVER_PORT << endl;
循环接收并处理消息
while (true)
{
int clientSockfd;
string clientIp;
int clientPort;
//接收客户端连接请求
clientSockfd = acceptClient(serverSockfd, &clientIp, &clientPort);
if (clientSockfd < 0)
{
cerr << "客户端连接失败!!!" << endl;
return -1;
}
cout << "accept client;client ip:" << clientIp << ",client port:" << clientPort << endl;
//处理客户端消息
doClient(clientSockfd, clientIp, clientPort, serverRtpSockfd, serverRtcpSockfd);
}
源代码
“main.h”
#include<iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
extern "C"
{
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
}
#define SERVER_PORT 8554
#define SERVER_RTP_PORT 55532
#define SERVER_RTCP_PORT 55533
#define BUF_MAX_SIZE (1024*1024)
//创建tcpsocket套接字
static int createTcpSocket()
{
int sockfd;
int on = 1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
return -1;
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
return sockfd;
}
//创建udpsocket套接字
static int createUdpSocket()
{
int sockfd;
int on = 1;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
return -1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
return sockfd;
}
//绑定地址和端口
static int bindSockerAddr(int sockfd, const char*ip, int port)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.S_un.S_addr = inet_addr(ip);
if (bind(sockfd, (sockaddr*)&addr, sizeof(sockaddr)) < 0)
return -1;
return 0;
}
//接收客户端连接请求
static int acceptClient(int sockfd, string *ip, int *port)
{
int clientfd;
int len = 0;
sockaddr_in addr;
//将addr初始化为0
memset(&addr, 0, sizeof(addr));
len = sizeof(addr);
clientfd = accept(sockfd, (sockaddr*)&addr, &len);
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.2.160");
if (clientfd < 0)
return -1;
*ip = inet_ntoa(addr.sin_addr);
*port = ntohs(addr.sin_port);
return clientfd;
}
//获取报文第一行数据
static char *getlinefrombuf(char*buf, char*line)
{
while (*buf != '\n')
{
*line = *buf;
line++;
buf++;
}
*line = '\n';
line++;
*line = '\0';
++buf;
return buf;
}
//处理OPTIONS方法
static int handleCmd_OPTIONS(char* result, int cseq)
{
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
"\r\n",
cseq);
return 0;
}
//处理DESCRIBE方法
static int handleCmd_DESCRIBE(char* result, int cseq, char* url)
{
char sdp[500];
char localIp[100];
sscanf_s(url, "rtsp://%[^:]:", localIp, sizeof(localIp));
sprintf_s(sdp, 256, "v=0\r\n"
"o=- 9%ld 1 IN IP4 %s\r\n"
"t=0 0\r\n"
"a=control:*\r\n"
"m=video 0 RTP/AVP 96\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=control:track0\r\n",
time(NULL), localIp);
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\nCSeq: %d\r\n"
"Content-Base: %s\r\n"
"Content-type: application/sdp\r\n"
"Content-length: %d\r\n\r\n"
"%s",
cseq,
url,
strlen(sdp),
sdp);
return 0;
}
//处理SETUP方法
static int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n"
"Session: 66334873\r\n"
"\r\n",
cseq,
clientRtpPort,
clientRtpPort + 1,
SERVER_RTP_PORT,
SERVER_RTCP_PORT);
return 0;
}
//处理PLAY方法
static int handleCmd_PLAY(char* result, int cseq)
{
sprintf_s(result, 256, "RTSP/1.0 200 OK\r\n"
"CSeq: %d\r\n"
"Range: npt=0.000-\r\n"
"Session: 66334873; timeout=60\r\n\r\n",
cseq);
return 0;
}
//消息处理函数
static void doClient(int clientSockfd, string clientIP, int clientPort,
int serverRtpSockfd, int serverRtcpSockfd)
{
char method[40];//方法
char url[100];//url
char version[40];//版本号
int cseq;//序列号
int clientRtpRort;//rtp端口号
int clientRtcpPort;//rtcp端口号
char *bufPtr;
char *rbuf = new char[BUF_MAX_SIZE];
char *sbuf = new char[BUF_MAX_SIZE];
char line[100];
while (true)
{
int recvlen;
//接收客户端请求
recvlen = recv(clientSockfd, rbuf, BUF_MAX_SIZE, 0);
if (recvlen <= 0)
goto out;
rbuf[recvlen] = '\0';//结尾加'\0'结束符
//打印报文
cout << "----------------------C->S----------------------\n" << endl;
cout << rbuf;
bufPtr = getlinefrombuf(rbuf, line);
//解析方法
if (sscanf_s(line, "%s %s %s\r\n", method, sizeof(method), url, sizeof(url), version, sizeof(version)) != 3)
{
cerr << "解析错误!!!" << endl;
goto out;
}
//解析序列号
bufPtr = getlinefrombuf(bufPtr, line);
if (sscanf_s(line, "CSeq: %d\r\n", &cseq) != 1)
{
cerr << "解析错误!!!" << endl;
goto out;
}
//如果是setup则再解析client_port
if (!strcmp(method, "SETUP"))
{
while (1)
{
bufPtr = getlinefrombuf(bufPtr, line);
if (!strncmp(line, "Transport:", strlen("Transport:")))
{
sscanf_s(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", &clientRtpRort, &clientRtpRort);
break;
}
}
}
if (!strcmp(method, "OPTIONS"))
{
if (handleCmd_OPTIONS(sbuf, cseq))
{
cerr << "无法处理OPTIONS" << endl;
goto out;
}
}
else if (!strcmp(method, "DESCRIBE"))
{
if (handleCmd_DESCRIBE(sbuf, cseq, url))
{
cerr << "无法处理DESCRIBE" << endl;
goto out;
}
}
else if (!strcmp(method, "SETUP"))
{
if (handleCmd_SETUP(sbuf, cseq, clientRtpRort))
{
cerr << "无法处理SETUP" << endl;
goto out;
}
}
else if (!strcmp(method, "PLAY"))
{
if (handleCmd_PLAY(sbuf, cseq))
{
cerr << "无法处理PLAY" << endl;
goto out;
}
}
else
{
goto out;
}
cout << "----------------------S->C----------------------\n" << endl;
cout << sbuf;
send(clientSockfd, sbuf, strlen(sbuf), 0);
}
out:
delete[]rbuf;
delete[]sbuf;
closesocket(clientSockfd);
}
“main.cpp”
#include "main.h"
int main()
{
//初始化库
WORD version = MAKEWORD(2, 2);//初始化套接字
WSADATA wsa;//初始化套接字
int is = WSAStartup(version, &wsa);
if (is != 0)
{
cerr << "初始化失败!!!" << endl;
WSACleanup();
return -1;
}
//版本校验
if (2 != HIBYTE(wsa.wVersion) || 2 != LOBYTE(wsa.wVersion))
{
//版本不匹配
WSACleanup();
return -1;
}
//创建套接字
int serverSockfd;
int serverRtpSockfd, serverRtcpSockfd;
int ret;
//创建tcp套接字
serverSockfd = createTcpSocket();
if (serverSockfd < 0)
{
cerr << "TCP套接字创建失败!!!" << endl;
return -1;
}
//绑定地址和端口
ret = bindSockerAddr(serverSockfd, "0.0.0.0", SERVER_PORT);
if (ret < 0)
{
cerr << "绑定地址或端口失败!!!" << endl;
return -1;
}
//监听网络端口
ret = listen(serverSockfd, 10);
if (ret < 0)
{
cerr << "监听端口失败!!!" << endl;
return -1;
}
//创建rtp udp套接字
serverRtpSockfd = createUdpSocket();
//创建rtcp udp套接字
serverRtcpSockfd = createUdpSocket();
if (serverRtcpSockfd < 0 || serverRtpSockfd < 0)
{
cerr << "udp创建失败!!!" << endl;
return -1;
}
if (bindSockerAddr(serverRtpSockfd, "0.0.0.0", SERVER_RTP_PORT) < 0 ||
bindSockerAddr(serverRtcpSockfd, "0.0.0.0", SERVER_RTCP_PORT) < 0)
{
cerr << "UDP套接字创建失败!!!" << endl;
return -1;
}
//cout << "rtsp://192.168.2.160:" << SERVER_PORT << endl;
cout << "rtsp://127.0.0.1:" << SERVER_PORT << endl;
while (true)
{
int clientSockfd;
string clientIp;
int clientPort;
//接收客户端连接请求
clientSockfd = acceptClient(serverSockfd, &clientIp, &clientPort);
if (clientSockfd < 0)
{
cerr << "客户端连接失败!!!" << endl;
return -1;
}
cout << "accept client;client ip:" << clientIp << ",client port:" << clientPort << endl;
//处理客户端消息
doClient(clientSockfd, clientIp, clientPort, serverRtpSockfd, serverRtcpSockfd);
}
}
运行
打开vlc输入rtsp://127.0.0.1:8554,这时控制台会输出交互信息,并且在wireshark回环抓包可以抓到交互报文
来自(52条消息) 从零开始写一个RTSP服务器(二)RTSP协议的实现_从零开始写一个rtsp服务器(二)_JT同学的博客-CSDN博客