首先看一下實現結果:
>>功能:
(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