0.前言
这是本学期的国产操作系统实验课的结课作业,虽然说是国产操作系统,但上的内容实际上是QT。本篇会先大致介绍一下我的界面是怎么搭的,然后介绍如何实现简单的局域网通讯。
1.界面设计

chatmain:主界面,包括显示聊天记录,用户名和ip地址,修改昵称和刷新局域网与用户。
messageitem:在主界面中的聊天记录,双击打开一个聊天框。
chatwindow:聊天框。
mymsg和yourmsg:聊天框中的气泡。




2.获得局域网用户
基于UDP协议,我们这里分为两步走。第一步,向局域网广播自己的存在。第二步,获得局域网中的用户(注意要排除掉自己)。为了不重复生成很多消息记录,导致消息发送到错误的聊天框上,我们使用一个map来保存生成过的messageitem,刷新时发现同样的ip直接使用之前的messageitem就可以了,不用再生成新的了。
//chatmain.h
private:
QHostAddress myIp;
QUdpSocket * udp_socket;
QTimer *tim;
QMap iplist;
以上变量需要添加到chatmain.h中,以下代码在chatmain的构造函数中。目的是获得本机ip。
//IP
QString localHostName=QHostInfo::localHostName(); //获取主机名
QHostInfo hostInfo=QHostInfo::fromName(localHostName);//本机的IP地址
QList listAdress=hostInfo.addresses(); //IP地址列表
if(!listAdress.isEmpty())
{
if(listAdress.size()>=1)
myIp=listAdress[1]; //如果主机的IP列表不为空,则使用其第一个IP地址,我电脑要第二个
else
myIp=listAdress.first(); //如果主机的IP列表不为空,则使用其第一个IP地址
}
ui->ip->setText(myIp.toString());
紧接着上面代码,我们创建socket并用799端口向局域网宣告自己的存在,这里我们创建了一个计时器每个1秒广播一次。
udp_socket = new QUdpSocket(this);
udp_socket->bind(799);
udp_socket->writeDatagram(myIp.toString().toStdString().c_str(),QHostAddress::Broadcast,799); // 发送广播消息
ui->avator->setStyleSheet(QString(defaultAvator)+"border-image:url(://"+QString("avator%1.jpg").arg(myIp.toIPv4Address()%20)+");");//设置头像
tim = new QTimer();
tim->setInterval(1000);
connect(tim,SIGNAL(timeout()),this,SLOT(onTimeOut()));
tim->start();
loadMessageItem();//程序启动获得当前局域网内的用户
onTimeOut函数十分简单,就是广播自己的存在。
void ChatMain::onTimeOut()
{
udp_socket->writeDatagram(myIp.toString().toStdString().c_str(),QHostAddress::Broadcast,799); // 发送广播消息
}
接下来就来看看怎么获得局域网用户,这里messageItem需要传入自己的ip,对方的ip还有消息时间等参数,这和你自己怎么实现有关。此外QListWidgetItem是向chatmain中的ListWidget添加项的,这也和你自己的实现有关。
void ChatMain::loadMessageItem(){
while(udp_socket->hasPendingDatagrams())
{
//qDebug()<<"read";
QByteArray data;
data.resize(udp_socket->pendingDatagramSize());
QHostAddress host;
quint16 port;
udp_socket->readDatagram(data.data(), data.size(), &host, &port);
QString text = QString("[%1:%2]:%3").arg(host.toString()).arg(port).arg(QString(data));
if(data==myIp.toString()) continue;
//得到时间
if(iplist.contains(data)){
//qDebug()<<"有了";
QListWidgetItem* pItem2 = new QListWidgetItem();
pItem2->setSizeHint(QSize(200,80));
ui->messageBox->insertItem(0, pItem2);
ui->messageBox->setItemWidget(pItem2, iplist[data]);
}
else{
//qDebug()<<"新建";
QDateTime current_date_time =QDateTime::currentDateTime();
QString current_date =current_date_time.toString("hh:mm");
MessageItem *b= new MessageItem(this,myIp.toIPv4Address()%20,QString("avator%1.jpg").arg(host.toIPv4Address()%20),data,data,current_date,false);
iplist.insert(data,b);
QListWidgetItem* pItem2 = new QListWidgetItem();
pItem2->setSizeHint(QSize(200,80));
ui->messageBox->insertItem(0, pItem2);
ui->messageBox->setItemWidget(pItem2, iplist[data]);
}
}
}
绑定好刷新键,这里清除会出现小问题,暂且不管了。
void ChatMain::on_f5PushButton_clicked()
{
//ui->messageBox->clear();
loadMessageItem();
}
2.聊天通讯
和之前差不多,我们将其作为练习留给读者——开个玩笑。
以下是chatwindow的构造函数中。
//局域网通讯
udp_socket = new QUdpSocket(this) ;
//绑定
udp_socket->bind(8888) ;
//当对方成功发送数据过来
//自动触发readyRead()
connect(udp_socket, SIGNAL(readyRead()), this, SLOT(getMessage())) ;
在按下发送按钮的同时要向对方ip发送消息。
udp_socket->writeDatagram(ui->MessagetextEdit->toPlainText().toUtf8(), QHostAddress(hisIp), 8888);
最后得到消息了就添加消息记录——注意构造函数中的connect。
void ChatWindow::getMessage()
{
qDebug()<<"getMessage";
char buf[1024] ={0};
QHostAddress client_address;
quint16 client_port;
//读取对方发送的内容
qint64 len = udp_socket->readDatagram(buf, sizeof(buf), &client_address, &client_port) ;
qDebug()<= 0)
{
QString str = QString("[%1:%2] %3").arg(client_address.toString())
.arg(client_port).arg(buf) ;
//添加项目
//得到时间
QDateTime current_date_time =QDateTime::currentDateTime();
QString current_date =current_date_time.toString("hh:mm");
//发送信号
emit newLastMessage(buf,current_date);
//设置该项高度
QFont font;
font.setFamily("Microsoft YaHei");
font.setPointSize(8);
QFontMetrics fm(font);
QRect rec = fm.boundingRect(buf);
qDebug()<myAvator,buf,rec.width());
QListWidgetItem* pItem = new QListWidgetItem();
pItem->setSizeHint(QSize(200,80));
ui->messageShower->addItem(pItem);
ui->messageShower->setItemWidget(pItem, ysg);
ui->messageShower->scrollToBottom();
//字符串所占的像素宽度,高度
pItem->setSizeHint(QSize(500,rec.width()/205*10+80));
//发送信号
}
}
救命稻草😭
过于有能(喜