为了做压力测试,客户端写了一个简单的代码,连上会议室后循环播放音频,保持一定时长后断开呼叫。
测试同学拿着程序去做并发测试,发现一台机器开启108个客户端进程后,总有些会失败。排除服务端问题后,就只能拿客户端日志去分析了,后面发现客户端有打印下面的错误日志:
在pjsip源码搜索“Unable to find appropriate RTP/RTCP ports combination”
定位到是D:\luobo\pjproject-2.12.1\pjsip\src\pjsua-lib\pjsua_media.c里create_rtp_rtcp_sock的实现里。
代码堆栈
走读create_rtp_rtcp_sock的实现
默认4000是在下面定义
这样问题原因可以定位到了:
程序未指定rtp的端口号,pjsip默认从4000开始查找。
第1个进程,默认4000给rtp,4001给rtcp
第2个进程,还是从4000开始查找,重试了1次, 使用了4002和4003。
第3个进程,还是从4000开始查找,重试了2次, 使用了4004和4005。
以此类推..
第100个进程,还是从4000开始查找,重试了99次,使用了4198和4199.
到第101个进程,还是从4000开始查找,重试了99次,都被占用了, 所以导致走了error逻辑。
解决办法是:
第101个进程开始指定端口从4200开始就好了,或者为了减少重试次数,每个进程指定端口号,依次递增2。
扩展:
1、这个测试程序pjsip客户端和服务端之间建立了3条连接,
4000是rtp端口,用来传输音频数据
4001是rtcp端口,用来传输音频控制信息
58501是sip端口,用来传输sip信令
日志里输出如下:
2、如果开启了视频,会另外建立2个连接,共5个连接
4000是rtp端口,用来传输音频数据
4001是rtcp端口,用来传输音频控制信息
4002是rtp端口,用来传输视频数据
4003是rtcp端口,用来传输视频控制信息
58501是sip端口,用来传输sip信令
3、将pjsip的日志输出到自己的日志里,
定义一个继承LogWriter的类MyLogWriter,实现write方法。
new一个MyLogWriter对象传给EpConfig.logConfig.writer,这里需要new,因为delete是在pjsip里面,如果是栈变量delete会崩溃。
class MyLogWriter : public LogWriter {
public:
MyLogWriter() {};
~MyLogWriter() {};
protected:
virtual void write(const LogEntry& entry) override {
MY_LOG(LOG_DEBUG, _T("%d %d %s %s"), entry.level, entry.threadId, utils::Utf8ToUnicode(entry.threadName).c_str(), utils::Utf8ToUnicode(entry.msg).c_str());
}
};MyEndpoint ep;
MyLogWriter* logWriter = new MyLogWriter;
ep.libCreate();// Init library
EpConfig ep_cfg;
ep_cfg.logConfig.level = 5;
ep_cfg.logConfig.msgLogging = true;
ep_cfg.logConfig.writer = logWriter;
4、sip连接创建的地方是在main函数入口,执行
MyEndpoint ep;
// Transport
TransportConfig tcfg;
//tcfg.port = 5060;
ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
5、setNullDev可以不使用音频物理设备,对于服务器不带音频设备的可以设置。
MyEndpoint ep;
ep.audDevManager().setNullDev();