实现的是用户登录既是服务器端也是客户端。
服务器端:创建UDP端口,绑定固定端口,用信号和槽机制来监听是否有数据来临,如果有,读取数据的消息类型,如果是新用户登录消息,读取新用户信息,在用户列表中添加用户信息,如果是聊天信息,读取信息,显示在聊天窗口中。如果是下线信息,就读取用户信息,用户列表中删除,流程如下:
客户端:登录时获取用户名、主机名和IP地址,并广播给局域网中的服务器,更新用户列表。当用聊天信息有发送时,将用户名、主机名和IP地址及聊天信息广播出去,流程如下:
程序代码如下:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
namespace Ui {
class Widget;
}
enum MessageType {Message, NewParticipant, ParticipantLeft, FileName, Refuse};
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void newParticipant(QString userName, QString localHostName, QString ipAddress);
void participantLeft(QString userNme, QString localHostName, QString time);
void sendMessage(MessageType type, QString serverAddress = "");
QString getIP();
QString getUserName();
QString getMessage();
private slots:
void prcessPendingDatagrams();
void on_sendButton_clicked();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket;
quint16 port;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QHostInfo>
#include <QMessageBox>
#include <QScrollBar>
#include <QDateTime>
#include <QNetworkInterface>
#include <QProcess>
#include <QTableWidgetItem>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
port = 45454;
udpSocket->bind(port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
connect(udpSocket,SIGNAL(readyRead()), this, SLOT(prcessPendingDatagrams()));
sendMessage(NewParticipant);
}
Widget::~Widget()
{
delete ui;
}
void Widget::sendMessage(MessageType type, QString serverAddress)
{
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
QString localHostName = QHostInfo::localHostName();
QString address = getIP();
out << type << getUserName() << localHostName;
switch (type) {
case Message:
if (ui->messageTextEdit->toPlainText() == "") {
QMessageBox::warning(0, QStringLiteral("警告"), QStringLiteral("发送内容不能为空"), QMessageBox::Ok);
return;
}
out << address << getMessage();
ui->messageBrowser->verticalScrollBar()->setValue(ui->messageBrowser->verticalScrollBar()->maximum());
break;
case NewParticipant:
out << address;
break;
case ParticipantLeft:
break;
case FileName:
break;
case Refuse:
break;
}
udpSocket->writeDatagram(data, data.length(), QHostAddress::Broadcast, port);
}
void Widget::prcessPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size());
QDataStream in(&datagram, QIODevice::ReadOnly);
int messageType;
in >> messageType;
QString userName, localHostName, ipAddress, message;
QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
switch (messageType) {
case Message:
in >> userName >> localHostName >> ipAddress >> message;
ui->messageBrowser->setTextColor(Qt::blue);
ui->messageBrowser->setCurrentFont(QFont("Times New Roman", 12));
ui->messageBrowser->append("[" + localHostName + "]" + time);
ui->messageBrowser->append(message);
break;
case NewParticipant:
in >> userName >> localHostName >> ipAddress;
newParticipant(userName, localHostName,ipAddress);
break;
case ParticipantLeft:
in >> userName >> localHostName;
participantLeft(userName, localHostName, time);
break;
case FileName:
case Refuse:
break;
}
}
}
void Widget::newParticipant(QString userName, QString localHostName, QString ipAddress)
{
bool isEmpty = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).isEmpty();
if (isEmpty) {
QTableWidgetItem* user = new QTableWidgetItem(userName);
QTableWidgetItem* host = new QTableWidgetItem(localHostName);
QTableWidgetItem* ip = new QTableWidgetItem(ipAddress);
ui->userTableWidget->insertRow(0);
ui->userTableWidget->setItem(0, 0, user);
ui->userTableWidget->setItem(0, 1, host);
ui->userTableWidget->setItem(0, 2, ip);
ui->messageBrowser->setTextColor(Qt::gray);
ui->messageBrowser->setCurrentFont(QFont("Times New Roman", 10));
ui->messageBrowser->append(QStringLiteral("%1 在线!").arg(userName));
ui->userNumLabel->setText(QStringLiteral("在线人数:%1").arg(ui->userTableWidget->rowCount()));
}
}
void Widget::participantLeft(QString userNme, QString localHostName, QString time)
{
int rowNum = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).first()->row();
ui->userTableWidget->removeRow(rowNum);
ui->messageBrowser->setTextColor(Qt::gray);
ui->messageBrowser->setCurrentFont(QFont("Times New Roman", 10));
ui->messageBrowser->append(QStringLiteral("%1 于 %2离开").arg(userNme).arg(time));
ui->userNumLabel->setText(QStringLiteral("在线人数:%1").arg(ui->userTableWidget->rowCount()));
}
QString Widget::getIP()
{
QList<QHostAddress> list = QNetworkInterface::allAddresses();
foreach(QHostAddress address, list) {
if (address.protocol() == QAbstractSocket::IPv4Protocol) return address.toString();
}
return 0;
}
QString Widget::getUserName()
{
QStringList envVariables;
envVariables << "USERNAME.*" << "USER.*" << "USERDOMAIN.*" << "HOSTNAME.*" << "DOMAINNAM.*";
QStringList environment = QProcess::systemEnvironment();
foreach (QString string, environment) {
int index = environment.indexOf(QRegExp(string));
if (index != -1) {
QStringList stringList = environment.at(index).split('=');
if (stringList.size() == 2) {
return stringList.at(1);
}
}
}
return "unknown";
}
QString Widget::getMessage()
{
QString msg = ui->messageTextEdit->toHtml();
ui->messageTextEdit->clear();
ui->messageTextEdit->setFocus();
return msg;
}
void Widget::on_sendButton_clicked()
{
sendMessage(Message);
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
注意:在qt中的信号与槽机制中,通过鼠标操作来关联槽,在生成的代码中是没有connect的,以按钮sendButton为例,生成的槽函数为on_sendButton_click(),当你在构造函数中再一次用connect关联时,会触发两次,导致会弹出“发送内容为空”的对话框。
|