我使用boost :: asio和smfl音频库启动了语音聊天服务器项目。我认为我的Mutex逻辑不正确,因为我会收到此错误:
Unhandled exception at 0x00B7871D in TCPAudioReceiver.exe: 0xC0000005: Access violation reading location 0x00000000.
调试器停在主的if(mServer.samplesAvailable())
线上,我还注意到服务器中的if(mSamplesQueue.front().size() > 256) return true;
行读取{???}
的大小。我认为锁定服务器的互惠件足以防止这种问题,但是我认为我仍然距离良好的设计距离一两个概念。
我没有出于特殊原因使用recursive_mutex,这是一个尝试在我的故障排除中更远的尝试。
您如何建议我解决这个问题?
主
#include "TcpAsyncServer.h"
#include "TcpAsyncSession.h"
#include "AudioStream.h"
#include <thread>
int main()
{
io_service ioService;
deque<vector<Int16>> mSamplesQueue;
SoundBuffer mSoundBuffer;
AudioStream mAudioStream;
cout << "Starting server..." << endl;
Server mServer(ioService, PORT);
thread serviceThread([&](){ioService.run();});
cout << "Starting reception loop..." << endl;
for(;;) {
{
lock_guard<recursive_mutex> lock(mServer.mServerMutex);
// Look for new samples
if(mServer.samplesAvailable()) { // <-- debugger stops here
cout << "Samples available..." << endl;
vector<Int16> wSamples;
mServer.getNextSamples(wSamples);
mSamplesQueue.push_back(wSamples);
}
}
// Loading and playing samples
if((mAudioStream.getStatus() != AudioStream::Playing) && !mSamplesQueue.empty()) {
cout << "Loading and playing audio stream..." << endl;
if(mSoundBuffer.loadFromSamples(
reinterpret_cast<Int16*>(mSamplesQueue.front().data()),
mSamplesQueue.front().size(), 2, 48000)
)
{
cout << "SoundBuffer loaded successfully..." << endl;
mAudioStream.load(mSoundBuffer);
mAudioStream.play();
mSamplesQueue.pop_front();
}
else cout << "SoundBuffer failed to load..." << endl;
}
// Give it some room to play the sound
while (mAudioStream.getStatus() == AudioStream::Playing) {
sleep(milliseconds(50));
}
}
serviceThread.join();
}
服务器标头
#include <mutex>
#include <deque>
#include <vector>
#include <iostream>
#include <boost/asio.hpp>
#include <SFML/Audio.hpp>
using namespace sf;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace std;
#define PORT 2112
#define SAMPLE_BUFFER 512
class Server
{
public:
// Ctor
Server(io_service & iService, short iPort);
// Methods
bool samplesAvailable();
void getNextSamples(vector<Int16> oSamples);
void pushSamples(vector<Int16> iSamples);
// Variables
recursive_mutex mServerMutex;
private:
// Methods
void doAccept();
// Variables
tcp::acceptor mAcceptor;
tcp::socket mSocket;
deque<vector<Int16>> mSamplesQueue;
};
服务器类
#include "TcpAsyncServer.h"
#include "TcpAsyncSession.h"
Server::Server(io_service & service, short port)
: mAcceptor(service, tcp::endpoint(tcp::v4(), port)),
mSocket(service) {
doAccept();
}
void Server::doAccept()
{
mAcceptor.async_accept(mSocket,
[this]
(boost::system::error_code error) {
if(!error) make_shared<Session>(move(mSocket),this)->start();
doAccept();
}
);
}
bool Server::samplesAvailable() {
lock_guard<recursive_mutex> lock(mServerMutex);
if(mSamplesQueue.front().size() > 256) return true; // <-- mSamplesQueue.front() has undefined size
return false;
}
void Server::getNextSamples(vector<Int16> oSamples) {
lock_guard<recursive_mutex> lock(mServerMutex);
oSamples = mSamplesQueue.front();
mSamplesQueue.pop_front();
}
void Server::pushSamples(vector<Int16> iSamples) {
lock_guard<recursive_mutex> lock(mServerMutex);
mSamplesQueue.push_back(iSamples);
}
会话标题
#include <iostream>
#include <mutex>
#include <deque>
#include <vector>
#include <boost/asio.hpp>
#include <SFML/Audio.hpp>
using namespace std;
using namespace sf;
using namespace boost::asio;
using namespace boost::asio::ip;
#define BUFFER_SIZE 1024
class Server;
class Session : public enable_shared_from_this<Session>
{
public:
// Ctor
Session(tcp::socket & iSocket, Server* iServerPtr);
// Methods
void start();
private:
// Methods
void doRead();
// Variables
tcp::socket mSocket;
char mData[BUFFER_SIZE];
Server* mServerPtr;
deque<vector<Int16>> mSampleBufferQueue;
};
会话课
#include "TcpAsyncSession.h"
#include "TcpAsyncServer.h"
Session::Session(tcp::socket & iSocket, Server* iServerPtr)
: mSocket(move(iSocket)),
mServerPtr(iServerPtr)
{}
void Session::start() {
doRead();
}
void Session::doRead() {
shared_ptr<Session> self(shared_from_this());
mSocket.async_read_some(buffer(mData,BUFFER_SIZE),
[this,self]
(boost::system::error_code error, size_t iBytesReceived) {
if(!error) {
cout << "Receiving " << iBytesReceived << " bytes..." << endl;
vector<Int16> wSamples;
for(unsigned int i = 0; i < iBytesReceived; i+=2) {
wSamples.push_back(static_cast<Int16>( mData[i]<<8 | mData[i] ));
}
{
lock_guard<recursive_mutex> lock(mServerPtr->mServerMutex);
mServerPtr->pushSamples(move(wSamples));
}
doRead();
}
}
);
}
哇,这很漫长。
您最明显的错误在这里:
if (mSamplesQueue.front().size() > 256)
return true; // <-- mSamplesQueue.front() has undefined size
您应该检查mSamplesQueue.empty()
是否不正确,否则使用front()
是UB。
而不是
bool Server::samplesAvailable() {
std::lock_guard<std::recursive_mutex> lock(mServerMutex);
if (mSamplesQueue.front().size() > 256)
return true;
return false;
}
您可以简单地写
bool Server::samplesAvailable() {
std::lock_guard<std::recursive_mutex> lock(mServerMutex);
return mSamplesQueue.size() && mSamplesQueue.front().size()>256;
}
审查:
-
中有UB
wSamples.push_back(static_cast<Int16>(mData[i] << 8 | mData[i]));
您需要括号,并且在许多编译器上都签署了
char
,这会导致UB左移。 切勿在标头文件中使用
using
-Directives。特别是不是using namespace std;
(为什么使用名称空间std"被认为是不良练习?(有很多不必要的缓冲区复制,为什么不只是重新诠释具有适当的endianness的平台上的
char[]
缓冲区和/或为什么pushSamples()
中的std::move
不在mSamplesQueue.push_back(std::move(iSamples));
直接使用类成员是一种代码气味(Demeter定律(。特别是,这是一个肯定的标志,当您写作时,您要混合抽象的水平:
{ std::lock_guard<std::recursive_mutex> lock(mServerPtr->mServerMutex); mServerPtr->pushSamples(std::move(wSamples)); }
特别是因为您已经有
void Server::pushSamples(std::vector<sf::Int16> iSamples) { std::lock_guard<std::recursive_mutex> lock(mServerMutex); mSamplesQueue.push_back(std::move(iSamples)); }
这意味着您目前需要递归静音,因为否则您将有一个僵局。
Session::mSampleBufferQueue
未使用getNextSamples
的签名是错误的。您永远不会看到任何效果,因为该参数是按值传递的。要么声明:void getNextSamples(std::vector<sf::Int16>& oSamples);
或
std::vector<sf::Int16> getNextSamples();
考虑制作诸如
samplesAvailable()
const之类的方法。为此,您需要标记Mutexmutable
(总是在C 11中声明std :: Mutex?
概念问题
从概念上讲,接受并发客户的接受存在问题。如果实际发生这种情况,您最终将随机播放流。
修复了UP版本
main.cpp
//#include "AudioStream.h" #include "TcpAsyncServer.h" #include "TcpAsyncSession.h" #include <thread> struct AudioStream { enum Status { Playing, Buffering }; Status getStatus() const { return Buffering; } void load(sf::SoundBuffer const& buf) { // std::cout << __PRETTY_FUNCTION__ << " rate=" << buf.getSampleRate() << " channels=" << buf.getChannelCount() << " duration=" << buf.getDuration().asSeconds() << "s " << " samples=" << buf.getSampleCount() << "n"; } void play() {} }; int main() { boost::asio::io_service ioService; std::deque<std::vector<sf::Int16> > mSamplesQueue; sf::SoundBuffer mSoundBuffer; AudioStream mAudioStream; std::cout << "Starting server..." << std::endl; Server mServer(ioService, PORT); // start async accept loop std::thread serviceThread([&]() { ioService.run(); }); std::cout << "Starting reception loop..." << std::endl; for (;;) { { std::lock_guard<std::recursive_mutex> lock(mServer.mServerMutex); // Look for new samples if (mServer.samplesAvailable()) { std::cout << "Samples available..." << std::endl; std::vector<sf::Int16> wSamples; mServer.getNextSamples(wSamples); mSamplesQueue.push_back(wSamples); } } // Loading and playing samples if ((mAudioStream.getStatus() != AudioStream::Playing) && !mSamplesQueue.empty()) { std::cout << "Loading and playing audio stream..." << std::endl; if (mSoundBuffer.loadFromSamples(reinterpret_cast<sf::Int16 *>(mSamplesQueue.front().data()), mSamplesQueue.front().size(), 2, 48000)) { std::cout << "SoundBuffer loaded successfully..." << std::endl; mAudioStream.load(mSoundBuffer); mAudioStream.play(); mSamplesQueue.pop_front(); } else std::cout << "SoundBuffer failed to load..." << std::endl; } // Give it some room to play the sound while (mAudioStream.getStatus() == AudioStream::Playing) { sleep(sf::milliseconds(50)); } } serviceThread.join(); }
tcpasyncserver.h
#include <SFML/Audio.hpp> #include <boost/asio.hpp> #include <deque> #include <iostream> #include <mutex> #include <vector> #define PORT 2113 #define SAMPLE_BUFFER 5120000 class Server { using tcp = boost::asio::ip::tcp; public: // Ctor Server(boost::asio::io_service &iService, short iPort); // Methods bool samplesAvailable() const; void getNextSamples(std::vector<sf::Int16>& oSamples); void pushSamples(std::vector<sf::Int16> iSamples); // Variables mutable std::recursive_mutex mServerMutex; private: // Methods void doAccept(); // Variables tcp::acceptor mAcceptor; tcp::socket mSocket; std::deque<std::vector<sf::Int16> > mSamplesQueue; };
tcpasyncsession.h
#include <boost/asio.hpp> #define BUFFER_SIZE 10240000 class Server; class Session : public std::enable_shared_from_this<Session> { using tcp = boost::asio::ip::tcp; public: // Ctor Session(tcp::socket &&iSocket, Server *iServerPtr); // Methods void start(); private: // Methods void doRead(); // Variables tcp::socket mSocket; uint8_t mData[BUFFER_SIZE]; Server *mServerPtr; };
tcpasyncserver.cpp
#include "TcpAsyncServer.h" #include "TcpAsyncSession.h" Server::Server(boost::asio::io_service &service, short port) : mAcceptor(service, tcp::endpoint(tcp::v4(), port)), mSocket(service) { mAcceptor.set_option(tcp::acceptor::reuse_address()); doAccept(); } void Server::doAccept() { mAcceptor.async_accept(mSocket, [this](boost::system::error_code error) { if (!error) std::make_shared<Session>(std::move(mSocket), this)->start(); doAccept(); }); } bool Server::samplesAvailable() const { std::lock_guard<std::recursive_mutex> lock(mServerMutex); return mSamplesQueue.size() && mSamplesQueue.front().size()>256; } void Server::getNextSamples(std::vector<sf::Int16>& oSamples) { std::lock_guard<std::recursive_mutex> lock(mServerMutex); oSamples = std::move(mSamplesQueue.front()); mSamplesQueue.pop_front(); } void Server::pushSamples(std::vector<sf::Int16> iSamples) { std::lock_guard<std::recursive_mutex> lock(mServerMutex); mSamplesQueue.push_back(std::move(iSamples)); }
tcpasyncsession.cpp
#include "TcpAsyncServer.h" #include "TcpAsyncSession.h" //#include <SFML/Audio.hpp> #include <iostream> Session::Session(tcp::socket &&iSocket, Server *iServerPtr) : mSocket(std::move(iSocket)), mServerPtr(iServerPtr) {} void Session::start() { doRead(); } void Session::doRead() { std::shared_ptr<Session> self(shared_from_this()); mSocket.async_read_some( boost::asio::buffer(mData, BUFFER_SIZE), [this, self](boost::system::error_code error, size_t iBytesReceived) { if (error) return; std::cout << "Receiving " << iBytesReceived << " bytes..." << std::endl; std::vector<sf::Int16> wSamples; for (unsigned int i = 0; i < iBytesReceived; i += 2) { wSamples.push_back(static_cast<sf::Int16>((mData[i] << 8) | mData[i])); } mServerPtr->pushSamples(std::move(wSamples)); doRead(); }); }
演示运行
喂养138m mp3:
Starting server...
Starting reception loop...
Receiving 8192 bytes...
Samples available...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=0.042666s samples=4096
Receiving 562829 bytes...
Samples available...
Receiving 745525 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=2.93141s samples=281415
Samples available...
Loading and playing audio stream...
Receiving 2815769 bytes...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=3.88295s samples=372763
Samples available...
Receiving 4978211 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=14.6655s samples=1407885
Samples available...
Receiving 5632954 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=25.9282s samples=2489106
Samples available...
Receiving 5893470 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=29.3383s samples=2816477
Samples available...
Receiving 5895401 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.6952s samples=2946735
Samples available...
Receiving 5894091 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.7052s samples=2947701
Samples available...
Receiving 5894197 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.6984s samples=2947046
Samples available...
Receiving 5894303 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.6989s samples=2947099
Samples available...
Receiving 5894144 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.6995s samples=2947152
Samples available...
Receiving 5896192 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.6987s samples=2947072
Samples available...
Receiving 5961675 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=30.7093s samples=2948096
Samples available...
Receiving 5961728 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0504s samples=2980838
Samples available...
Receiving 5960615 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0507s samples=2980864
Samples available...
Receiving 5960793 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0449s samples=2980308
Samples available...
Receiving 5960668 bytes...
Loading and playing audio stream...
An internal OpenAL call failed in SoundBuffer.cpp(265).
Expression:
alBufferData(m_buffer, format, &m_samples[0], size, sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0458s samples=2980397
Samples available...
Receiving 5960740 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0451s samples=2980334
Samples available...
Receiving 5960668 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0455s samples=2980370
Samples available...
Receiving 5960740 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0451s samples=2980334
Samples available...
Receiving 5960668 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0455s samples=2980370
Samples available...
Receiving 5960740 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0451s samples=2980334
Samples available...
Receiving 5960668 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0455s samples=2980370
Samples available...
Receiving 5960740 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0451s samples=2980334
Samples available...
Receiving 5960668 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0455s samples=2980370
Samples available...
Receiving 5960740 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0451s samples=2980334
Samples available...
Receiving 4770135 bytes...
Loading and playing audio stream...
SoundBuffer loaded successfully...
void AudioStream::load(const sf::SoundBuffer &) rate=48000 channels=2 duration=31.0455s samples=2980370
Samples available...
Loading and playing audio stream...
SoundBuffer loaded successfully...
除了我的模拟AudioStream
不能正确初始化音频图的事实外,对我来说看起来不错。
请注意,我增加了缓冲区的大小,因此日志不会太大。