2021 시작

서버 기능 :

클라로부터 받은 데이터를 서버의 로그에 올려주고 서버와 연결된 모든 클라이언트에 다시 뿌린다.(그냥 말만 번지르르한 평범하면서도 모자란 서버)

 

//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()
{
}

tcp_server_chat2.zip
0.01MB

 

 

v0.1에서 말했듯이 sender라는 함수를 이용해서 만들어 봤어 그런데 문제가 있어 스레드에서 connect한 것들이 신호를 보내면 그것도 스레드로 실행될 줄 알았는데 아니었더라 그걸 다 만들고 나서야 알아버렸어! ㅠㅠ

뭐 결과적으로는 실패라는 거지 나중에 다시 한번 만들어봐야겠어

NewConnect객체를 RecvThread객체 안에 넣는식으로 만들어야 할거같아(아마도......)

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading