Khuyến mãi vps

Xây dựng ứng dụng Qt ChatRoom đơn giản

#1
1. Giới thiệu

Bài viết này minh họa xây dựng chương trình ChatRoom đơn giản nhằm mục đích tìm hiểu kỹ thuật lập trình mạng (socket) sử dụng các lớp thư viện Qt hỗ trợ, gồm:

- Lớp QTcpServer: hỗ trợ tạo một server, lắng nghe, quản lý và phục vụ các kết nối (gửi, nhận dữ liệu)

- Lớp QTcpSocket: tạo kết nối socket, quản lý các kết nối, gửi/nhận dữ liệu từ socket được thiết lập.

Ứng dụng gồm 2 phần:

- Chương trình ChatServer: Quản lý các client kết nối đến. Nhận các thông điệp và gửi broadcast lại các thông điệp này đến tất cả các client.



- Chương trình ChatClient: Kết nối đến Server, gửi các thông điệp đến server và nhận lại các thông điệp được server broadcast lại.


2. Xây dựng chương trình ChatServer

Mã nguồn chatserver.h

#ifndef CHATSERVER_H

#define CHATSERVER_H

#include <QWidget>

#include <QTcpServer>

#include <QList>

#include <QHash>

class QBuffer;

class QTcpSocket;

namespace Ui {

class ChatServer;

}

class ChatServer : public QWidget

{

Q_OBJECT

public:

explicit ChatServer(QWidget *parent = 0);

~ChatServer();

private:

QTcpServer *server;

//Danh sach chua cac connection de phuc vu client ket noi den

QList<QTcpSocket*> connections;

//Mang bo dem du lieu, moi phan tu tuong ung du lieu cua mot socket

QHash<QTcpSocket*, QBuffer*> buffers;

Ui::ChatServer *ui;

private slots: //Tao slots tu dong

void on_btnStop_clicked();

void on_btnStart_clicked();

private slots: //Tao bang cach viet ma

void addConnection(); //Xu ly khi co mot client ket noi den

void removeConnection(); //Xu ly khi co mot client ngat ket noi

void receiveMessage(); //Nhan thong diep tu cac client

};

#endif // CHATSERVER_H


Mã nguồn chatserver.cpp

#include "chatserver.h"

#include "ui_chatserver.h"

#include <QHostAddress>

#include <QTcpSocket>

#include <QBuffer>

static const int DEFAULT_PORT = 6789;

int port;

QHostAddress server_addr;

ChatServer::ChatServer(QWidget *parent) :

QWidget(parent),

ui(new Ui::ChatServer)

{

ui->setupUi(this);

server = new QTcpServer();

//Dang ky signal/slot cho su kien co client ket noi den

connect(server, SIGNAL(newConnection()), this, SLOT(addConnection()));

server_addr = QHostAddress::LocalHost; //Lay dia chi may host

//server_addr = QHostAddress::Any;

port = DEFAULT_PORT; //Cong mac dinh

ui->lineEditIP->setText(server_addr.toString()); //Hien dia chi nay len lineEdit

ui->lineEditIP->isEnabled();

ui->lineEditPort->setText(QString::number(port));

}

ChatServer::~ChatServer()

{

delete ui;

}

void ChatServer::eek:n_btnStart_clicked()

{

port = ui->lineEditPort->text().toInt();

//server bat dau lang nghe ket noi

bool b = server->listen(server_addr, port);

if(b)

{

qDebug("server started");

ui->btnStart->setEnabled(false);

ui->btnStop->setEnabled(true);

}

else

{

qDebug("server can't start! check IP or Port");

}

}

void ChatServer::eek:n_btnStop_clicked()

{

if(server->isListening()) //Neu server dang lang nghe

{

server->close(); //thi dung

ui->btnStart->setEnabled(true);

ui->btnStop->setEnabled(false);

}

}

/*******************************************************

Ham xu ly su kien khi co client ket noi den

Thuc hien:

- Tao socket phuc vu ket noi va them vao danh sach quan ly

- Tao bo dem du lieu tuong ung cho socket do va them vao danh sach quan ly

********************************************************/

void ChatServer::addConnection()

{

QTcpSocket* connection = server->nextPendingConnection();

connections.append(connection); //Them ket noi vao danh sach

QBuffer *buffer = new QBuffer(this); //Tao bo dem du lieu cho connection nay

buffer->open(QIODevice::ReadWrite);

buffers.insert(connection, buffer); //Luu vao danh sach

//Dang ky signal/slot cho moi connection khi co su kien disconnect hoac du lieu gui toi

connect(connection, SIGNAL(disconnected()), this, SLOT(removeConnection()));

connect(connection, SIGNAL(readyRead()), this, SLOT(receiveMessage()));

}

/*******************************************************

Ham xu ly su kien khi co client ngat ket noi

Thuc hien:

- Lay socket cua ket noi can ngat

- Lay bo dem du lieu dang quan ly

- Thuc hien ngat ket noi va giai phong bo dem du lieu

********************************************************/

void ChatServer::removeConnection()

{

//Lay socket gui su kien ngat ket noi

QTcpSocket* socket = static_cast<QTcpSocket*>(sender());

QBuffer *buffer = buffers.take(socket); //Lay bo dem du lieu cua ket noi nay (tu danh sach bo dem)

buffer->close(); //Ket thuc gui nhan

buffer->deleteLater();//giai phong bo dem

connections.removeAll(socket); //Xoa socket nay khoi danh sach ket noi server dang quan ly

socket->deleteLater(); //Giai phong socket

}

/*******************************************************

Ham xu ly su kien khi co du lieu san sang nhan tu mot socket

********************************************************/

void ChatServer::receiveMessage()

{

//Xac dinh ket noi nao co thong diep den

QTcpSocket* socket = static_cast<QTcpSocket*>(sender());

//Lay bo dem du lieu tuong ung voi socket (tu bang hash buffers)

QBuffer *buffer = buffers.value(socket);

//ghi toan bo du lieu cua socket nay vao bo dem (buffer) da xac dinh o tren

qint64 bytes = buffer->write(socket->readAll()); //bytes chua kich thuoc ghi thanh cong

//dich chuyen ve dau bo dem bang ham seek

buffer->seek(buffer->pos() - bytes);

//Vong lap doc tung dong (line) cua thong diep chua trong bo dem

while (buffer->canReadLine())

{

QByteArray line = buffer->readLine();//doc tung dong

//Gui broadcast den tat ca cac ket noi dang quan ly

foreach (QTcpSocket* connection, connections)

{

connection->write(line); //Gui bang cach ghi ra socket

}

}

}


3. Xây dựng chương trình ChatClient

- Thiết kế giao diện như hình minh họa


Sử dụng kỹ thuật layout:

- grid layout cho nhóm điều khiển ở trên cùng (labelServer, labelNick, lineEditServer, lineEditNick, spinBoxPort, btnConnect)

- horizontal layout cho nhóm điều khiển ở dưới cùng (labelMsg, lineEditMsg, btnSend)

- Sau đó, sử dụng vertical layout cho cả form (gồm 2 nhóm trên và điều khiển textEditChat ở giữa).

3.1. Mã nguồn chatclient.h

#ifndef CHATCLIENT_H

#define CHATCLIENT_H

#include <QWidget>

#include <QBuffer>

#include <QTcpSocket>

namespace Ui {

class ChatClient;

}

class ChatClient : public QWidget

{

Q_OBJECT

public:

explicit ChatClient(QWidget *parent = 0);

~ChatClient();

//Khai bao cac ham slot

private slots:

void setConnected();

void setDisconnected();

void toggleConnection();

void sendMessage();

void receiveMessage();

private:

Ui::ChatClient *ui;

QBuffer *buffer;

QTcpSocket *socket;

};

#endif // CHATCLIENT_H

3.2. Mã nguồn chatclient.cpp

#include "chatclient.h"

#include "ui_chatclient.h"

static const int DEFAULT_PORT = 6789;

ChatClient::ChatClient(QWidget *parent) :

QWidget(parent),

ui(new Ui::ChatClient)

{

ui->setupUi(this);

//Khoi tao cho cac dieu khien (widget)

ui->spinBoxPort->setRange(1000,32767);

ui->spinBoxPort->setValue(DEFAULT_PORT);

ui->lineEditServer->setText("localhost");

ui->lineEditNick->setText("hungpn");

//Khai bao socket va buffer du lieu

socket = new QTcpSocket(this);

buffer = new QBuffer(this);

buffer->open(QIODevice::ReadWrite);

//Dang ky signal/slot cho cac su kien

//Su kien click nut Connect

connect(ui->btnConnect, SIGNAL(clicked()), this, SLOT(toggleConnection()));

//Su kien go message va enter (gui di)

connect(ui->lineEditMsg, SIGNAL(returnPressed()), this, SLOT(sendMessage()));

//Hoac click nut Send (gui di)

connect(ui->btnSend, SIGNAL(clicked()), this, SLOT(sendMessage()));

//Su kien ket noi Socket thanh cong

connect(socket, SIGNAL(connected()), this, SLOT(setConnected()));

//Su kien ngat ket noi

connect(socket, SIGNAL(disconnected()), this, SLOT(setDisconnected()));

//Su kien san sang nhan du lieu

connect(socket, SIGNAL(readyRead()), this, SLOT(receiveMessage()));

//Ban dau chua ket noi

setDisconnected();

}

ChatClient::~ChatClient()

{

delete ui;

}

/*************************************************

Ham xu ly cac dieu khien khi ket noi thanh cong

**************************************************/

void ChatClient::setConnected()

{

ui->lineEditServer->setEnabled(false);

ui->spinBoxPort->setEnabled(false);

ui->lineEditNick->setEnabled(true);

ui->lineEditMsg->setEnabled(true);

ui->textEditChat->setEnabled(true);

ui->textEditChat->clear();

ui->btnSend->setEnabled(true);

ui->btnConnect->setText("Disconnect");

}

void ChatClient::setDisconnected()

{

ui->lineEditServer->setEnabled(true);

ui->spinBoxPort->setEnabled(true);

ui->lineEditNick->setEnabled(false);

ui->lineEditMsg->setEnabled(false);

ui->textEditChat->setEnabled(false);

ui->textEditChat->clear();

ui->btnSend->setEnabled(false);

ui->btnConnect->setText("Connect");

}

/*************************************************

Ham tat/bat ket noi den server

**************************************************/

void ChatClient::toggleConnection()

{

if(socket->state() == QAbstractSocket::UnconnectedState)

{

socket->connectToHost(ui->lineEditServer->text(), ui->spinBoxPort->value());

}

else

{

socket->disconnectFromHost();

}

}

void ChatClient::sendMessage()

{

// "<nick> message\n"

QString nick = ui->lineEditNick->text().toLatin1();

QString msg = ui->lineEditMsg->text().toLatin1();

//socket->write("<" + nick + "> " + msg + "\n");

socket->write("<" + ui->lineEditNick->text().toLatin1() + "> " + ui->lineEditMsg->text().toLatin1() + "\n");

ui->lineEditMsg->clear();

}

void ChatClient::receiveMessage()

{

// missing some checks for returns values for the sake of simplicity

qint64 bytes = buffer->write(socket->readAll());

// go back as many bytes as we just wrote so that it can be read

buffer->seek(buffer->pos() - bytes);

// read only full lines, line by line

while (buffer->canReadLine())

{

QString line = buffer->readLine();

ui->textEditChat->append(line.simplified());

}

}

4. Một số lớp thư viện Qt sử dụng trong ứng dụng

- Chương trình ChatServer có sử dụng một số lớp thư viện Qt quan trọng để hỗ trợ xử lý là:

· Lớp QList để quản lý một danh sách phần tử

· Lớp QBuffer cung cấp bộ đệm dữ liệu

· Lớp QHash quản lý một từ điển (dictionary) dựa trên bảng hash

4.1. Lớp QList:

QList<QTcpSocket*> connections;

Tạo ra một danh sách connections để quản lý các kết nối từ client đến. Một kết nối mới (là một socket) sẽ được thêm vào danh sách này khi có sự kiện

newConnection và sẽ được xóa khỏi danh sách khi có sự kiện ngắt kết nối (disconnected)

//Thêm một kết nối mới

QTcpSocket* connection = server->nextPendingConnection();

connections.append(connection); //Them ket noi vao danh sach

//Xóa một kết nối khỏi danh sách khi bị ngắt

QTcpSocket* socket = static_cast<QTcpSocket*>(sender());

//...

connections.removeAll(socket);

4.2. Lớp QHash

Lớp QHash cung cấp một từ điển dựa trên bảng hash.

Qhash<Key, T> là một lớp chứa cặp (key, value) và cung cấp cơ chế để truy cập nhanh vào value (giá trị) liên kết với một key (khóa)

Khai báo:

QHash<QTcpSocket*, QBuffer*> buffers;

Tạo ra một bảng hash có tên buffers để quản lý các cặp <socket, buffer>

Trong đó: socket là kết nối được tạo ra cho mỗi client kết nối đến server và buffer là bộ đệm (đối tượng lớp Qbuffer) để chứa dữ liệu gửi/nhận trên socket đó.

Câu lệnh:

buffers.insert(connection, buffer); //Luu vao danh sach

Sẽ chèn thêm một cặp <connection, buffer> vào bảng hash buffers để quản lý các kết nối và dữ liệu của nó.

Để lấy bộ đệm dữ liệu ứng với một kết nối (socket):

//Lay bo dem du lieu cua socket nay

QBuffer *buffer = buffers.value(socket);

4.3. Lớp QBuffer

Lớp này cung cấp cơ chế để giao tiếp với một mảng dữ liệu kiểu byte (QByteArray) sử dụng giao diện QIODevice

Trong hàm ChatServer::receiveMessage() xử lý sự kiện nhận thông điệp từ Client

//Xac dinh ket noi nao co thong diep den

QTcpSocket* socket = static_cast<QTcpSocket*>(sender());

//Lay bo dem du lieu tuong ung voi socket (tu bang hash buffers)

QBuffer *buffer = buffers.value(socket);

//ghi toan bo du lieu cua socket nay vao bo dem (buffer) da xac dinh o tren

qint64 bytes = buffer->write(socket->readAll()); //bytes chua kich thuoc ghithanh cong

//dich chuyen ve dau bo dem bang ham seek

buffer->seek(buffer->pos() - bytes);

//Vong lap doc tung dong (line) cua thong diep chua trong bo dem

while (buffer->canReadLine())

{

QByteArray line = buffer->readLine();//doc tung dong

//Gui broadcast den tat ca cac ket noi dang quan ly

foreach (QTcpSocket* connection, connections)

{

connection->write(line); //Gui bang cach ghi ra socket

}

}
 
Khuyến mãi vps

ADS

Bài mới

Top