서버 기능 :
클라로부터 받은 데이터를 서버의 로그에 올려주고 서버와 연결된 모든 클라이언트에 다시 뿌린다.(그냥 말만 번지르르한 평범하면서도 모자란 서버)
//recv_thread.h
#ifndef RECV_THREAD_H
#define RECV_THREAD_H
#include <QSet>
#include <QStringListModel>
#include <QStringList>
#include <QListView>
#include <QVBoxLayout>
#include <QTcpSocket>
#include <QTcpServer>
#include <QHostAddress>
#include <QtNetwork>
#include <QDebug>
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
#include <QWaitCondition>
class RecvThread : public QThread
{
Q_OBJECT
private:
//리스트 모델
QSharedPointer<QStringListModel>list_model;
//들어온 소켓을 모아놓음
//QSet<QTcpSocket*>socket_list;
QSharedPointer<QSet<QTcpSocket*>>socket_list;
//동기화를 위해 사용
//QMutex mutex;
QSharedPointer<QMutex>mutex;
//리스트 모델에 적용하기 위해 사용
//QStringList str_list;
QSharedPointer<QStringList>str_list;
public:
RecvThread(QSharedPointer<QStringListModel>&list_model,
QSharedPointer<QMutex>&mutex,
QSharedPointer<QStringList>&str_list,
QSharedPointer<QSet<QTcpSocket*>>&socket_list);
public slots:
void readMessage(); //데이터가 들어오면 호출
void SendMessage();
};
#endif // RECV_THREAD_H
//recv_thread.cpp
#include "recv_thread.h"
//데이터가 들어오면
RecvThread::RecvThread(QSharedPointer<QStringListModel>&list_model,
QSharedPointer<QMutex>&mutex,
QSharedPointer<QStringList>&str_list,
QSharedPointer<QSet<QTcpSocket*>>&socket_list)
:list_model(list_model),
mutex(mutex),
str_list(str_list),
socket_list(socket_list)
{}
void RecvThread::readMessage()
{
//sender는 시그널을 보낸 객체를 리턴함
QTcpSocket* tcp_socket = (QTcpSocket*)sender();
//받은 데이터가 0이상이라면
if(tcp_socket->bytesAvailable()>= 0)
{
QMutexLocker locker(mutex.get());
//클라이언트로부터 받은 데이터를
//listmodel에 넣어 listView에 뿌려준다.
QByteArray str= tcp_socket->readAll();
*str_list<<str;
this->list_model->setStringList(*str_list);
QSet<QTcpSocket*>::iterator iter = socket_list->begin();
for(;iter != socket_list->end();iter++)
{
(*iter)->write(str_list->back().toStdString().c_str());
}
}
return;
}
void RecvThread::SendMessage()
{
QTcpSocket* tcp_socket = static_cast<QTcpSocket*>(sender());
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//Widget.h <--- mainWidget
#ifndef WIDGET_H
#define WIDGET_H
#include "recv_thread.h"
#include <QWidget>
//새로운 연결이 들어오는 것을 관리하는 객체
class NewConnect : public QThread
{
Q_OBJECT
private:
QSharedPointer<QTcpServer>tcp_server;
QSharedPointer<QSet<QTcpSocket*>>socket_list;
QSharedPointer<QStringList>str_list;
QSharedPointer<QStringListModel>list_model;
//RecvThread의 동기화를 담당
QSharedPointer<QMutex>mutex;
QSharedPointer<QWaitCondition>condition;
//리시브 스레드
QSharedPointer<RecvThread>recv_thread;
//해당 클래스(NewConnect)의 동기화를 담당
QMutex this_mutex;
protected:
void run() override //새로운 연결이 생길 시 start()함수로 호출됨
{
QTcpSocket *tcp_socket = tcp_server->nextPendingConnection();
if(!tcp_socket)
{
qDebug()<<QString("nextPendingConnection Error : %1\r\n")
.arg(tcp_server->errorString());
}
//socket_list변수를 위한 동기화
QMutexLocker locker(&this_mutex);
//리스트에 추가
socket_list->insert(tcp_socket);
//연결이 끊길 시 동작
connect(tcp_socket,SIGNAL(disconnected()),
this,SLOT(disconnected()));
//클라이언트로부터 데이터가 도착하면 동작
connect(tcp_socket,SIGNAL(readyRead()),
recv_thread.get(),SLOT(readMessage()));
}
public:
NewConnect(QSharedPointer<QTcpServer>&tcp_server,
QSharedPointer<QStringListModel>&list_model,
QSharedPointer<QMutex>&mutex,
QSharedPointer<QStringList>&str_list,
QSharedPointer<QSet<QTcpSocket*>>&socket_list,
QSharedPointer<QWaitCondition>&condtion)
:tcp_server(tcp_server),
list_model(list_model),
mutex(mutex),
str_list(str_list),
socket_list(socket_list),
condition(condition)
{
recv_thread.reset(new RecvThread(list_model, //리스트 모델 갱신 때 사용
mutex, //동기화때 사용
str_list, //리스트 모델에 데이터 넣을 때 사용
socket_list //소켓 모아놓는곳
));
}
public slots:
//void disconnected(); //연결이 끊기면 호출
void disconnected()//연결이 끊기면 호출
{
//SIGNAL을 보낸 객체 반환
QTcpSocket* tcp_socket = static_cast<QTcpSocket*>(sender());
socket_list->remove(tcp_socket);
tcp_socket->close();
qDebug()<<"disconnect";
}
};
//////////////////////
//////////////
///////////////////
class Widget : public QWidget
{
Q_OBJECT
private:
//GUI부분
QSharedPointer<QStringListModel> list_model;
QSharedPointer<QListView>list_view;
QSharedPointer<QVBoxLayout>layout;
//Network
QSharedPointer<QTcpServer>tcp_server;
//스레드 장착!
QSharedPointer<RecvThread>my_thread;
QSharedPointer<NewConnect>new_connect;
//동기화를 위해 사용
QSharedPointer<QMutex> mutex;
QSharedPointer<QWaitCondition>condition;
//리스트 모델에 적용하기 위해 사용
QSharedPointer<QStringList> str_list;
//소켓 모아놓는 곳
QSharedPointer<QSet<QTcpSocket*>>socket_list;
//스레드 환경에서 소켓 생성해주는 소켓
public:
Widget(QWidget *parent = nullptr);
~Widget();
};
#endif // WIDGET_H
//Widget.cpp
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
///////////////////////GUI///////////////////////////////
setFixedSize(500,700);
//리스트 모델
list_model.reset(new QStringListModel);
list_view.reset(new QListView);
list_view->setModel(list_model.get());
layout.reset(new QVBoxLayout(this));
layout->addWidget(list_view.get());
//레리아웃 설정
setLayout(layout.get());
//////////////////////////////////////////////////////////////
//리스트 모델 데이터 역할
str_list.reset(new QStringList);
//server 객체
tcp_server.reset(new QTcpServer(this));
//동기화
mutex.reset(new QMutex);
condition.reset(new QWaitCondition);
//소켓 모아놓는 곳
socket_list.reset(new QSet<QTcpSocket*>);
new_connect.reset(new NewConnect(tcp_server,
list_model,//리스트 모델에 데이터 넣을 때 사용
mutex, //동기화때 사용
str_list, //리스트 모델 갱신 때 사용
socket_list,//소켓 모아놓는곳
condition));
//서버가 접속하길 기다리는 중
if(!tcp_server->listen(QHostAddress::Any,25000))
{
qDebug()<<QString("Listen Error : %1\r\n")
.arg(tcp_server->errorString());
}
//새로운 연결이 들어오면 스레드로 처리한다.
connect(tcp_server.get(),SIGNAL(newConnection()),
new_connect.get(),SLOT(start()));
}
Widget::~Widget()
{
}
v0.1에서 말했듯이 sender라는 함수를 이용해서 만들어 봤어 그런데 문제가 있어 스레드에서 connect한 것들이 신호를 보내면 그것도 스레드로 실행될 줄 알았는데 아니었더라 그걸 다 만들고 나서야 알아버렸어! ㅠㅠ
뭐 결과적으로는 실패라는 거지 나중에 다시 한번 만들어봐야겠어
NewConnect객체를 RecvThread객체 안에 넣는식으로 만들어야 할거같아(아마도......)