上一篇文章记录了本次聊天室项目服务端的实现过程和主要代码,本篇文章将记录客户端的实现过程和主要代码。
至于Tcp的原理,上篇文章已经提到过了,所以在这里就不多啰嗦了。
还是一样的套路,上来先初始化控件对象和设置布局,这些都不是重点,可以自己随意布局。
//设置窗口标题
setWindowTitle(tr("TCP Client"));
//初始化控件对象
contentListWidget = new QListWidget;
userNameLabel = new QLabel(tr("name:"));
userNameLineEdit = new QLineEdit;
serverIPLabel = new QLabel(tr("IP:"));
serverIPLineEdit = new QLineEdit;
portLabel = new QLabel(tr("port:"));
portLineEdit = new QLineEdit;
enterBtn = new QPushButton(tr("Enter the chat room!"));
sendLineEdit = new QLineEdit;
sendBtn = new QPushButton(tr("Send!"));
mainLayout = new QGridLayout(this);
//设置布局
mainLayout->addWidget(userNameLabel,0,0);
mainLayout->addWidget(userNameLineEdit,0,1);
mainLayout->addWidget(serverIPLabel,1,0);
mainLayout->addWidget(serverIPLineEdit,1,1);
mainLayout->addWidget(portLabel,2,0);
mainLayout->addWidget(portLineEdit,2,1);
mainLayout->addWidget(enterBtn,3,0,1,2);
mainLayout->addWidget(contentListWidget,4,0,1,2);
mainLayout->addWidget(sendLineEdit,5,0);
mainLayout->addWidget(sendBtn,5,1);
至于执行出来的效果呢,就是下面这个样子,有点丑陋,哈哈。
接下来就是一些关键函数的实现了,我会把一些说明写到代码的注释当中
//记录当前状态,true表示已经进入聊天室,false表示不在聊天室中,根据status的值判断是进入还是离开聊天室
status = false;
//指定端口
port = 80;
//将端口显示到控件上
portLineEdit->setText(QString::number(port));
//初始化记录IP地址的对象
serverIP = new QHostAddress();
//将按钮的点击信号和想要对应的槽函数连接起来
connect(enterBtn,SIGNAL(clicked(bool)),this,SLOT(slotEnter()));
connect(sendBtn,SIGNAL(clicked(bool)),this,SLOT(slotSend()));
//将发送信息按钮设置为不可点击状态
sendBtn->setEnabled(false);
接下来实现两个槽函数
首先是加入enterBtn对应的加入聊天室的槽函数
这段代码有点长,主要还是按照当前是否已经连接到聊天室的两种情况分别进行处理
slotEnter():
if(!status){//判断当前状态,是否已经加入到聊天室中
//获取控件上输入的IP地址
QString ip = serverIPLineEdit->text();
if(!serverIP->setAddress(ip)){//判断获取的IP地址是否能够正常被解析
//如果不能,则弹出警告
QMessageBox::information(this,tr("error"),tr("ip error!"));
return;
}
if(userNameLineEdit->text()==""){//判断控件中的用户名是否为空
//如果用户名为空,则发出警告
QMessageBox::information(this,tr("error"),tr("userName error!"));
return;
}
//将控件中的用户名保存到之前定义的变量当中
userName = userNameLineEdit->text();
//初始化Tcp套接字,并各个信号和对应的槽函数连接起来
tcpSocket = new QTcpSocket(this);
connect(tcpSocket,SIGNAL(connected()),this,SLOT(slotConnected()));
connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
//与TCP服务端进行连接,成功的话会发送connected()信号
tcpSocket->connectToHost(*serverIP,port);
//记录当前连接状态,记录为已进入聊天室
status = true;
}else{//如果已经连接到了服务端,执行面下的操作
//定义一个变量,用来保存信息的长度
int length = 0;
//定义一条离开聊天室的信息,离开聊天室时要发送这条信息给服务端
QString msg = userName+tr(":Leave chat Room!");
//通知服务端,我要离开聊天室了,也就是发送上面那条信息给服务端
//如果发送成功,则会返回信息的长度,通过这种方式来判断,信息是否发送出去了
if((length = tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length()){
return;
}
//与服务端断开连接后,会发出disconnected()信号
tcpSocket->disconnectFromHost();
//保存当前状态,记录为已经离开聊天室
status = false;
}
当要连接服务端的时候发送connected()信号,触发slotConnected()槽函数,下面是槽函数的实现
slotConnected():
//将发送信息按钮设置为可点击状态
sendBtn->setEnabled(true);
//将加入聊天室按钮的文字信息更改为leave
enterBtn->setText(tr("Leave!"));
//发送消息给服务端,通知服务端我要加入聊天室了,具体流程跟上面发送信息的流程相同,这里就不重复说明了
int length = 0;
QString msg = userName+tr(":Enter chat Room!");
if((length = tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length()){
return;
}
当与服务端断开连接的时候发送disconnected()信号,触发slotDisconnected()槽函数,代码如下
slotDisconnected():
//既然已经离开聊天室了,发送信息的按钮当然也就不能点啦
sendBtn->setEnabled(false);
//然后进入聊天室按钮的信息也要修改一下
enterBtn->setText(tr("Enter the chat room!"));
当有数据到来时,就会触发dataReceived()槽函数,从套接字中取出有效数据并显示出来
dataReceived():
//循环条件是等待读取的字节数大于0,也就是说还有数据没有读出来
while(tcpSocket->bytesAvailable()>0){
QByteArray datagram;
//统一一下长度
datagram.resize(tcpSocket->bytesAvailable());
//开始读取有效数据
tcpSocket->read(datagram.data(),datagram.size());
//把读取到的数据转化为QString类型,方便显示
QString msg = datagram.data();
//把消息显示到控件上就ok了
contentListWidget->addItem(msg.left(datagram.size()));
}
最后要实现的是点击发送消息按钮时触发的槽函数slotSend()
slotSend():
if(sendLineEdit->text()==""){//判断输入框中是否输入了消息
return;
}
//把你的用户名拼接到消息前面,让其他用户知道是谁发送的消息
QString msg = userName+":"+sendLineEdit->text();
//开始向套接字中写入数据
tcpSocket->write(msg.toLatin1(),msg.length());
//最后,消息都发送出去了,肯定要把输入框里面的文字清空掉啦
sendLineEdit->clear();
就这样,本次做的练习就结束了,这次练习主要是熟悉Tcp协议连接,断开连接,读取消息,发送消息的流程和实现方法。
这次练习之后会进行一段时间的吸收,后续可能会做一个融入更多自己理解的案例,如果有时间的话,哈哈,但是学习的步伐是不会停下来的,希望各位大佬能够多多指教,多多和我交流。