1. // widget.h
    2. #include <QWidget>
    3. #include <QUdpSocket>
    4. QT_BEGIN_NAMESPACE
    5. namespace Ui { class Widget; }
    6. QT_END_NAMESPACE
    7. class Widget : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Widget(QWidget *parent,QString name);
    12. ~Widget();
    13. void closeEvent(QCloseEvent *);
    14. private:
    15. Ui::Widget *ui;
    16. signals:
    17. void closeWidget(); // 关闭窗口发送信号
    18. public:
    19. enum MsgType {Msg,UsrEnter,UsrLeft};
    20. void sndMsg(MsgType type); // 广播UDP消息
    21. void usrEnter(QString userName); // 处理新用户加入
    22. void usrLeft(QString userName); // 处理用户离开
    23. QString getUsrName(); // 获取用户名
    24. QString getMsg(); // 获取聊天信息
    25. private:
    26. QUdpSocket* udpSocket; // Udp套接字
    27. qint16 port; // 端口
    28. QString uName; // 用户名
    29. void recvMsg(); // 接收Udp消息
    30. };
    1. // widget.cpp
    2. #include "widget.h"
    3. #include "ui_widget.h"
    4. #include <QDataStream>
    5. #include <QMessageBox>
    6. #include <QDateTime>
    7. Widget::Widget(QWidget *parent,QString name)
    8. : QWidget(parent)
    9. , ui(new Ui::Widget)
    10. {
    11. ui->setupUi(this);
    12. // 初始化操作
    13. udpSocket = new QUdpSocket(this); // 套接字
    14. uName = name; // 用户名
    15. port = 9999; // 端口号
    16. /*
    17. * ShareAddress模式, 允许不同的服务连接到相同的地址和端口,常用于多客户端监听同一个服务器端口
    18. * ReuseAddressHint模式, 断线重新连接服务器
    19. */
    20. udpSocket->bind(port,QUdpSocket::ShareAddress|QUdpSocket::ReuseAddressHint); // 绑定端口号
    21. //sndMsg(UsrEnter); // 发送新用户进入
    22. // 点击发送按钮发送消息
    23. connect(ui->Btn_Send,&QPushButton::clicked,[=](){
    24. sndMsg(Msg);
    25. });
    26. // 监听别人发送的数据
    27. connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::recvMsg);
    28. }
    29. Widget::~Widget()
    30. {
    31. delete ui;
    32. }
    33. void Widget::closeEvent(QCloseEvent*)
    34. {
    35. emit closeWidget();
    36. }
    37. void Widget::sndMsg(Widget::MsgType type)
    38. {
    39. // 发送的消息分为3种类型
    40. // 发送的数据分3段处理. 第一段,类型; 第二段,用户名; 第三段,具体内容
    41. QByteArray array;
    42. QDataStream stream(&array,QIODevice::WriteOnly);
    43. stream << type << getUsrName(); // 类型 和 用户名, 添加到流中
    44. switch (type) {
    45. case Msg: // 普通消息发送
    46. if(ui->msgTxtEdit->toPlainText() == "")
    47. { // 如果用户没有输入内容,不发消息
    48. QMessageBox::warning(this,"警告","发送内容不能为空");
    49. return;
    50. }
    51. stream << getMsg(); // 具体内容, 添加到流中
    52. break;
    53. case UsrEnter:
    54. break;
    55. case UsrLeft:
    56. break;
    57. default:
    58. break;
    59. }
    60. // 书写报文
    61. udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
    62. }
    63. QString Widget::getUsrName()
    64. {
    65. return uName;
    66. }
    67. QString Widget::getMsg()
    68. {
    69. QString str = ui->msgTxtEdit->toHtml();
    70. ui->msgTxtEdit->clear(); // 清空输入框
    71. ui->msgTxtEdit->setFocus();
    72. return str;
    73. }
    74. void Widget::recvMsg()
    75. {
    76. // 获取报文长度
    77. qint64 size = udpSocket->pendingDatagramSize();
    78. // 读取报文
    79. QByteArray array = QByteArray(size,0);
    80. udpSocket->readDatagram(array.data(),size);
    81. // 解析数据
    82. QDataStream stream(&array,QIODevice::ReadOnly); // 从array中读取数据到stream中
    83. // 定义接收三段数据的三个变量
    84. int msgType;
    85. QString usrName;
    86. QString msg;
    87. stream >> msgType; // 从stream中取出 类型
    88. // 获取当前时间
    89. QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh-mm-ss");
    90. switch (msgType) {
    91. case Msg:
    92. stream >> usrName >> msg; // 从stream中取出 用户名 和 具体内容
    93. // 追加聊天记录
    94. ui->msgBrowser->setTextColor(Qt::blue); // 设置聊天框文本颜色
    95. ui->msgBrowser->append("[" + usrName + "]" + time);
    96. ui->msgBrowser->append(msg);
    97. break;
    98. case UsrEnter:
    99. break;
    100. case UsrLeft:
    101. break;
    102. default:
    103. break;
    104. }
    105. }