网络摄像头第二篇(远程服务端)

1、作用

远程服务器网络摄像头中起的是一个桥接的作用,将摄像头服务端摄像头客户端连接起来。正如网络摄像头第一篇(原理)所介绍的一样,当摄像头服务端启动之后,会主动与远程服务端进行连接,这时候,远程服务器会将摄像头服务端的ip地址和端口进行保存起来。当摄像头客户端启动之后,远程服务器会将摄像头客户端的ip和地址发送给摄像头服务端

2、服务器界面

2.1、界面

在这里插入图片描述

2.2、服务器放在局域网

在这里插入图片描述

2.3、服务器放在外网

在这里插入图片描述

3、NetServerTCP 实现

3.1、NetServerTCP 头文件

#ifndef NETSERVERTCP_H
#define NETSERVERTCP_H

#ifndef INETSERVERTCP_H
#include "network/inetservertcp.h"
#endif /*INETSERVERTCP_H*/
#include <thread>

BEGIN_DOX_NAMESPACE

FORWARD_DECLARE_CLASS(INetServerReactor)

class NetServerTCP
	: public INetServerTCP
{
	REGISTER_ACTUALIZE(NetServerTCP, INetServerTCP)

public:

	/* @接口 默认构造函数
	 * @类名 [NetServerTCP]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年3月14号
	 */
	NetServerTCP();

	/* @接口 默认构造函数
	 * @类名 [NetServerTCP]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年3月14号
	 */
	~NetServerTCP();

	/* @接口
	 * @类名 [INetServer]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年4月19日
	 */
	virtual void stopServer();

	/* @接口 等待连接
	 * @参数 int 连接的个数
	 * @类名 [IFTPNetServer]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年4月19日
	 */
	virtual bool waitConnect(int);

	/* @接口 初始化端口
	 * @参数 int 默认端口21
	 * @参数 int 客户端的个数
	 * @类名 [INetProtocol]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年4月19日
	 */
	virtual bool initIPAndPort(int);

	/* @接口
	 * @类名 [INetServer]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年4月19日
	 */
	virtual void startServer(RecvCallBackFunc);

	/* @接口
	 * @类名 [INetServer]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年4月21日
	 */
	virtual void setBufferSize(int bufSize = 40960);

	/* @接口 发送消息
	 * @参数 QString 发送消息
	 * @类名 [INetServer]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年4月20日
	 */
	virtual bool sendMessage(ulong, const QString &);

private:

	/* @接口
	 * @类名 [NetServerTCP]
	 * @邮箱 575814050@qq.com
	 * @时间 2020年11月18日
	 */
	void initNetServerTCP();

private:

	ulong m_sock; std::thread m_thread;
	QList<Object<INetServerReactor>> m_reactors;

};

END_DOX_NAMESPACE

#endif /*NETSERVERTCP_H*/

3.2 、NetServerTCP源文件

#ifndef NETSERVERTCP_H
#include "netservertcp.h"
#endif /*NETSERVERTCP_H*/
#include "winsock2.h"
#include "network/inetmacro.h"
#include "manager/iappmanager.h"
#include "network/inetserverreactor.h"
#include <thread>

BEGIN_DOX_NAMESPACE

NetServerTCP::NetServerTCP()
	: INetServerTCP()
{
	WSADATA wsaData;
	if(0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
	{
		DoxLoggerFL("Winsock Initialization failed");
		ExitProcess(0);
	}
	m_sock = socket(PF_INET, SOCK_STREAM, 0);
	initNetServerTCP();
}

NetServerTCP::~NetServerTCP()
{
	closesocket(m_sock);
}

void NetServerTCP::stopServer()
{

}

void NetServerTCP::initNetServerTCP()
{
	Object<IAppManager> iAppManager(NIL);
	QStringList clss = iAppManager->classListFromIID(INetServerReactor::ClassGuid());
	for(int idx = 0; idx < clss.length(); ++idx)
	{
		QString cls = clss[idx];
		Object<INetServerReactor> iReactor(cls.toLocal8Bit().constData());
		if(iReactor.valid()) m_reactors.append(iReactor);
	}
}

bool NetServerTCP::waitConnect(int count)
{
	if(SOCKET_ERROR == listen(m_sock, count))
	{
		DoxLoggerFL("监听套接字失败");
		return false;
	}
	return true;
}

bool NetServerTCP::initIPAndPort(int port)
{
	SOCKADDR_IN servAdr;
	memset(&servAdr, 0, sizeof(servAdr));
	servAdr.sin_family = AF_INET;
	servAdr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAdr.sin_port = htons(port);
	if(SOCKET_ERROR == bind(m_sock, (SOCKADDR *)&servAdr, sizeof(servAdr)))
	{
		DoxLoggerFL("绑定端口失败");
		return false;
	}
	return true;
}

void NetServerTCP::setBufferSize(int bufSize)
{

}

bool NetServerTCP::sendMessage(ulong, const QString &)
{
	return false;
}

void NetServerTCP::startServer(RecvCallBackFunc backFunc)
{
	auto acceptCB = [this]()
	{
		while(true)
		{
			SOCKADDR_IN clntAdr; int clntAdrSize = sizeof(clntAdr);
			SOCKET hClntSock = accept(m_sock, (SOCKADDR *)&clntAdr, &clntAdrSize);
			for(int idx = 0; idx < m_reactors.length(); ++idx)
				m_reactors[idx]->acceptClient(hClntSock);
		}
	};
	m_thread =std::thread(acceptCB);
}

END_DOX_NAMESPACE

3.3、解释

NetServerTCP(); 构造中初始化套接字的版本
initNetServerTCP(); 用来初始化服务器反应器(反应器说白了就是一组回调构成的类,每个回调都是一个接口)
initIPAndPort(); 初始化套接字地址以及绑定套接字
waitConnect(); 用来设定套接字监听的个数
startServer(); 让套接字处于监听状态,注意在这个函数里面引用了lambda表达式,这个表示式是一个线程函数。当套接字被客户端连接上时,accept就会返回,然后开始进入反应器结合,这个反应器开始调用acceptClient这个接口,这个接口是在其他的dll中实现,通过这种反应器技术,我们可以将其他dll中的函数直接在这里调用。

INetServerReactor 这是一个反应器接口类,里面定义了一组接口,只要实现类继承了这个接口并且被注册到框架中,那么每当客户端连接到服务器时,都会主动执行这个接口类中的acceptClient接口。

4、ClientsList 实现(实现了INetServerReactor反应器)

4.1、ClientsList 头文件

#ifndef CLIENTSLIST_H
#define CLIENTSLIST_H

#include "widget/iwidget.h"
#include "QtWidgets/QListView"
#include "network/inetserverreactor.h"

FORWARD_DECLARE_CLASS(QStandardItemModel)

BEGIN_DOX_NAMESPACE

class ClientsList
	: public IWidget
	, public QListView
	, public INetServerReactor
{
	REGISTER_ACTUALIZES(ClientsList, IWidget, INetServerReactor)

public:

	/* @接口 默认构造函数
	 * @类名 [ClientsList]
	 * @作者 杨发荷
	 * @邮箱 575814050@qq.com
	 * @时间 2020年3月14号
	 */
	ClientsList();

	/* @接口 默认构造函数
	 * @类名 [ClientsList]
	 * @作者 杨发荷
	 * @邮箱 575814050@qq.com
	 * @时间 2020年3月14号
	 */
	~ClientsList();

	/* @接口
	 * @类名 [ClientsList]
	 * @作者 杨发荷
	 * @邮箱 575814050@qq.com
	 * @时间 2020年11月17日
	 */
	virtual ObjectPtr selfWidget();

	/* @接口
	 * @类名 [ClientsList]
	 * @作者 杨发荷
	 * @邮箱 575814050@qq.com
	 * @时间 2020年11月18日
	 */
	virtual bool acceptClient(ulong);

	/* @接口
	 * @类名 [ClientsList]
	 * @作者 杨发荷
	 * @邮箱 575814050@qq.com
	 * @时间 2020年11月18日
	 */
	virtual QString windowTitle() const;

private:

	/* @接口
	 * @类名 [ClientsList]
	 * @作者 杨发荷
	 * @邮箱 575814050@qq.com
	 * @时间 2020年11月18日
	 */
	void initClientsList();

private:

	QStandardItemModel *m_model;

};

END_DOX_NAMESPACE

#endif /*CLIENTSLIST_H*/

4.2、ClientList 源文件

#include "winsock2.h"
#include "clientslist.h"
#include "widget/imainwindow.h"
#include "QtWidgets/QMessageBox"
#include "QtGui/QStandardItemModel"

BEGIN_DOX_NAMESPACE

ClientsList::ClientsList()
	: QListView(WIDGET_PARENT)
	, IWidget(), INetServerReactor()
{
	initClientsList();
}

ClientsList::~ClientsList()
{

}

ObjectPtr ClientsList::selfWidget()
{
	return DOX_DYNAMIC_PTR(QWidget);
}

void ClientsList::initClientsList()
{
	m_model = new QStandardItemModel;
	QListView::setModel(m_model);
	QListView::resize(200, 600);
	Object<IMainWindow> iMainWindow(NIL);
	iMainWindow->resize(int2(200, 600));
	IWindowFlags wflags = iMainWindow->windowFlags();
	iMainWindow->windowFlags(wflags & ~wfWindowMinMaxButtonsHint);
}

bool ClientsList::acceptClient(ulong sock)
{
	SOCKADDR_IN address;
	int nAddrLen = sizeof(address);
	memset(&address, 0, nAddrLen);
	//根据套接字获取地址信息
	if(::getpeername(sock, (SOCKADDR *)&address, &nAddrLen) != 0) return false;
	QString ip = ::inet_ntoa(address.sin_addr);
	u_short port = ntohs(address.sin_port);
	QString item = QS("%1:%2").arg(ip).arg(port);
	m_model->appendRow(new QStandardItem(item));
	return true;
}

QString ClientsList::windowTitle() const
{
	return QS("客户端列表");
}

END_DOX_NAMESPACE

4.3、解释

ClientsList(); 构造函数
initClientsList(); 用来初始化QListView列表以及框架标题和大小
acceptClient(); 通过客户端套接字,获取客户端IP地址和端口号(这个接口也就是在3.2小节startServer中被调用)

5、调用

5.1、注册类

#include "clientslist.h"
#include "netservercmd.h"
#include "core/modulemacro.h"

#pragma comment (lib,"ws2_32.lib")

BEGIN_DOX_NAMESPACE
BEGIN_DEFINE_DOX_MODULE("NetServer")
	XDEFINE_CLASSMAP_ENTRY_SINGLE(ClientsList)
FINISH_DEFINE_DOX_MODULE()
END_DOX_NAMESPACE

OUTCAPI bool attachDoxygen()
{
	return dox::initApplication();
}

OUTCAPI bool detachDoxygen()
{
	return true;
}

5.2、模块初始化

#include "netservercmd.h"

#include "winsock2.h"
#include "core/idoxcommand.h"
#include "network/inetmacro.h"
#include "widget/imainwindow.h"
#include "widget/idockwidget.h"
#include "network/inetmanager.h"
#include "network/inetservertcp.h"

BEGIN_DOX_NAMESPACE

void netServerUi()
{
	Object<IMainWindow> iMainWindow(NIL);
	Object<IWidget> iWidget("NetServer.ClientsList");
	iMainWindow->setWidget(iWidget);
}

void startServer()
{
	Object<INetManager> iNetManager(NIL);
	Object<INetServer> iNetServer = iNetManager->netServer(ProtocolTCP);
	iNetServer->initIPAndPort(1119); iNetServer->waitConnect(64);
	iNetServer->startServer(NULL); iNetServer->retainObject();
}

bool initApplication()
{
	Object<IDoxCommand> command(NIL); netServerUi(); 
	command->registerCommand("NetServer", "startServer", startServer);
	return true;
}

END_DOX_NAMESPACE

首先先将通过netServerUi接口将ClientsList加到主界面中从而显示2.1小节中的主界面,然后通过执行startserver这个命令,启动服务器服务。接下来就是等待摄像头端的连接。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值