首先看一下实现结果:
>>功能:
(1)服务器和客户端之间进行聊天通信;
(2)一个服务器可同时给多个客户端发送消息;(全部连接时)
也可以只给特定的客户端发送消息;(连接特定IP)
(3)可发送任意字符,包括中文;(原来参考的程序不能发中文)
>>后续拓展:
(1)不同客户端之间能否进行通信?
(2)发完消息之后如何清空发送区?
(3)如何完善登录注册功能?
(4)如何更换界面背景及颜色?
…………
程序:
注意:首先需要在每个工程.pro文件里加上一句
QT += network
1.mytcpclient.h
#ifndef MYTCPCLIENT_H #define MYTCPCLIENT_H #include <QMainWindow> #include <QTcpSocket> #include <QHostAddress> #include <QMessageBox> namespace Ui { class MyTcpClient; } class MyTcpClient : public QMainWindow { Q_OBJECT public: explicit MyTcpClient(QWidget *parent = 0); ~MyTcpClient(); private: Ui::MyTcpClient *ui; QTcpSocket *tcpClient; private slots: //客户端槽函数 void ReadData(); void ReadError(QAbstractSocket::SocketError); void on_btnConnect_clicked(); void on_btnSend_clicked(); void on_btnClear_clicked(); }; #endif // MYTCPCLIENT_H
2.mytcpclient.cpp
#include "mytcpclient.h" #include "ui_mytcpclient.h" MyTcpClient::MyTcpClient(QWidget *parent) : QMainWindow(parent), ui(new Ui::MyTcpClient) { ui->setupUi(this); //初始化TCP客户端 tcpClient = new QTcpSocket(this); //实例化tcpClient tcpClient->abort(); //取消原有连接 ui->btnConnect->setEnabled(true); ui->btnSend->setEnabled(false); connect(tcpClient, SIGNAL(readyRead()), this, SLOT(ReadData())); connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(ReadError(QAbstractSocket::SocketError))); } MyTcpClient::~MyTcpClient() { delete ui; } void MyTcpClient::ReadData() { //QByteArray buffer = tcpClient->readAll(); //by me QByteArray buffer; //add by me buffer.resize(tcpClient->bytesAvailable());//add by me tcpClient->read(buffer.data(),buffer.size());//add by me if(!buffer.isEmpty()) { QString msg = QString::fromLocal8Bit(buffer.data());//add by me ui->edtRecv->append(msg); //buffer -> msg ; by me } } void MyTcpClient::ReadError(QAbstractSocket::SocketError) { tcpClient->disconnectFromHost(); ui->btnConnect->setText(tr("连接")); QMessageBox msgBox; msgBox.setText(tr("failed to connect server because %1").arg(tcpClient->errorString())); msgBox.exec(); } void MyTcpClient::on_btnConnect_clicked() { if(ui->btnConnect->text()=="连接") { tcpClient->connectToHost(ui->edtIP->text(), ui->edtPort->text().toInt()); if (tcpClient->waitForConnected(1000)) // 连接成功则进入if{} { ui->btnConnect->setText("断开"); ui->btnSend->setEnabled(true); } } else { tcpClient->disconnectFromHost(); if (tcpClient->state() == QAbstractSocket::UnconnectedState || tcpClient->waitForDisconnected(1000)) //已断开连接则进入if{} { ui->btnConnect->setText("连接"); ui->btnSend->setEnabled(false); } } } void MyTcpClient::on_btnSend_clicked() { QString data = ui->edtSend->toPlainText(); QByteArray text = data.toLocal8Bit(); //add by me if(data != "") { //tcpClient->write(data.toLatin1()); //qt5出去了.toAscii() //by me tcpClient->write(text,text.length()); //add by me } } void MyTcpClient::on_btnClear_clicked() { ui->edtRecv->clear(); }
3.mytcpserver.h
#ifndef MYTCPSERVER_H #define MYTCPSERVER_H #include <QMainWindow> #include <QTcpServer> #include <QTcpSocket> #include <QNetworkInterface> #include <QMessageBox> namespace Ui { class MyTcpServer; } class MyTcpServer : public QMainWindow { Q_OBJECT public: explicit MyTcpServer(QWidget *parent = 0); ~MyTcpServer(); private: Ui::MyTcpServer *ui; QTcpServer *tcpServer; QList<QTcpSocket*> tcpClient; QTcpSocket *currentClient; private slots: void NewConnectionSlot(); void disconnectedSlot(); void ReadData(); void on_btnConnect_clicked(); void on_btnSend_clicked(); void on_btnClear_clicked(); }; #endif // MYTCPSERVER_H
4.mytcpserver.cpp
#include "mytcpserver.h" #include "ui_mytcpserver.h" MyTcpServer::MyTcpServer(QWidget *parent) : QMainWindow(parent), ui(new Ui::MyTcpServer) { ui->setupUi(this); tcpServer = new QTcpServer(this); ui->edtIP->setText(QNetworkInterface().allAddresses().at(1).toString()); //获取本地IP ui->btnConnect->setEnabled(true); ui->btnSend->setEnabled(false); connect(tcpServer, SIGNAL(newConnection()), this, SLOT(NewConnectionSlot())); } MyTcpServer::~MyTcpServer() { delete ui; } // newConnection -> newConnectionSlot 新连接建立的槽函数 void MyTcpServer::NewConnectionSlot() { currentClient = tcpServer->nextPendingConnection(); tcpClient.append(currentClient); ui->cbxConnection->addItem(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1]) .arg(currentClient->peerPort())); connect(currentClient, SIGNAL(readyRead()), this, SLOT(ReadData())); connect(currentClient, SIGNAL(disconnected()), this, SLOT(disconnectedSlot())); } // 客户端数据可读信号,对应的读数据槽函数 void MyTcpServer::ReadData() { // 由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端 for(int i=0; i<tcpClient.length(); i++) { QByteArray buffer; //add by me buffer.resize(tcpClient[i]->bytesAvailable());//add by me tcpClient[i]->read(buffer.data(),buffer.size());//add by me //QByteArray buffer = tcpClient[i]->readAll(); //by me if(buffer.isEmpty()) continue; static QString IP_Port, IP_Port_Pre; IP_Port = tr("[%1:%2]:").arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]) .arg(tcpClient[i]->peerPort()); // 若此次消息的地址与上次不同,则需显示此次消息的客户端地址 if(IP_Port != IP_Port_Pre) ui->edtRecv->append(IP_Port); QString msg = QString::fromLocal8Bit(buffer.data());//add by me ui->edtRecv->append(msg); //buffer -> msg ; by me //更新ip_port IP_Port_Pre = IP_Port; } } // disconnected -> disconnectedSlot 客户端断开连接的槽函数 void MyTcpServer::disconnectedSlot() { //由于disconnected信号并未提供SocketDescriptor,所以需要遍历寻找 for(int i=0; i<tcpClient.length(); i++) { if(tcpClient[i]->state() == QAbstractSocket::UnconnectedState) { // 删除存储在combox中的客户端信息 ui->cbxConnection->removeItem(ui->cbxConnection->findText(tr("%1:%2") .arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]) .arg(tcpClient[i]->peerPort()))); // 删除存储在tcpClient列表中的客户端信息 tcpClient[i]->destroyed(); tcpClient.removeAt(i); } } } // 监听--断开 void MyTcpServer::on_btnConnect_clicked() { if(ui->btnConnect->text()=="监听") { bool ok = tcpServer->listen(QHostAddress::Any, ui->edtPort->text().toInt()); if(ok) { ui->btnConnect->setText("断开"); ui->btnSend->setEnabled(true); } } else { for(int i=0; i<tcpClient.length(); i++)//断开所有连接 { tcpClient[i]->disconnectFromHost(); bool ok = tcpClient[i]->waitForDisconnected(1000); if(!ok) { // 处理异常 } tcpClient.removeAt(i); //从保存的客户端列表中取去除 } tcpServer->close(); //不再监听端口 ui->btnConnect->setText("监听"); ui->btnSend->setEnabled(false); } } // 发送数据 void MyTcpServer::on_btnSend_clicked() { QString data = ui->edtSend->toPlainText(); QByteArray text = data.toLocal8Bit(); //add by me if(data == "") return; // 文本输入框为空时 //全部连接 if(ui->cbxConnection->currentIndex() == 0) { for(int i=0; i<tcpClient.length(); i++) //tcpClient[i]->write(data.toLatin1()); //qt5除去了.toAscii() //by me tcpClient[i]->write(text,text.length()); //add by me } //指定连接 else { QString clientIP = ui->cbxConnection->currentText().split(":")[0]; int clientPort = ui->cbxConnection->currentText().split(":")[1].toInt(); // qDebug() << clientIP; // qDebug() << clientPort; for(int i=0; i<tcpClient.length(); i++) { if(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]==clientIP && tcpClient[i]->peerPort()==clientPort) { //tcpClient[i]->write(data.toLatin1()); //by me tcpClient[i]->write(text,text.length()); //add by me return; //ip:port唯一,无需继续检索 } } } } void MyTcpServer::on_btnClear_clicked() { ui->edtRecv->clear(); }
5.mytcpclient.ui
6.mytcpserver.ui