一、UDP与TCP的区别
用一个表格来显示这两者的区别
比较项 | TCP | UDP |
是否连接 | 面向连接 | 无连接 |
传输是否可靠 | 可靠 | 不可靠 |
流量控制 | 提供 | 不提供 |
工作方式 | 全双工 | 可以是全双工 |
应用场合 | 大量数据 | 少量数据 |
速度 | 慢 | 快
|
我们这里采用TCP
//当有新的连接出现时就会调用此函数,和newConnection功能类似
void Server::incomingConnection(int socketDescriptor)
{
TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);//只要有新的连接就生成一个新的通信套接字
//将新创建的通信套接字描述符指定为参数socketdescriptor
tcpclientsocket->setSocketDescriptor(socketDescriptor);
//将这个套接字加入客户端套接字列表中
tcpclientsocketlist.append(tcpclientsocket);
//接收到tcpclientsocket发送过来的更新界面的信号
connect(tcpclientsocket, SIGNAL(updateserver(QString,int)), this, SLOT(slotupdateserver(QString,int)));
connect(tcpclientsocket, SIGNAL(clientdisconnected(int)), this, SLOT(slotclientdisconnect(int)));
}
这个函数我就不多说了,注释我写的很清楚了
二、客户端程序,
在Qt中往往客户端比较简单,这里只要一个类即可,继承QTcpSocket
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include
#include
namespace Ui {
class TcpClient;
}
class TcpClient : public QWidget
{
Q_OBJECT
public:
explicit TcpClient(QWidget *parent = 0);
~TcpClient();
private slots:
//用来处理连接成功的信号
void slotconnectedsuccess();
//接收服务器传过来的信息
void slotreceive();
//用来处理离开聊天室的信号
void slotdisconnected();
//发送按钮的槽函数
void on_Send_clicked();
//进入离开聊天室按钮
void on_pushButtonEnter_clicked();
private:
Ui::TcpClient *ui;
//用来判断是否进入了聊天室
bool status;
int port;
QHostAddress *serverIP;
QString userName;
QTcpSocket *tcpsocket;
};
#endif // TCPCLIENT_H
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include
#include
#include
TcpClient::TcpClient(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpClient)
{
ui->setupUi(this);
//将进入聊天室的标志位置为false
status = false;
//端口为8888
port = 5555;
ui->Lineport->setText(QString::number(port));//界面中端口默认显示8888
serverIP = new QHostAddress();
//未进入聊天室内不能发送信息
ui->Send->setEnabled(false);
}
TcpClient::~TcpClient()
{
delete ui;
}
//用来处理连接成功的信号
void TcpClient::slotconnectedsuccess()
{
//进入聊天室可以发送信息了
ui->Send->setEnabled(true);
//将进入聊天的按钮改为离开聊天室
ui->pushButtonEnter->setText("离开聊天室");
int length = 0;
//将用户名发送给服务器
QString msg= userName + " :Enter Chat Room";
tcpsocket->write(msg.toUtf8().data());
}
void TcpClient::slotreceive()
{
QByteArray array = tcpsocket->readAll();
ui->textEdit->append(array);
}
void TcpClient::slotdisconnected()
{
ui->Send->setEnabled(false);
ui->pushButtonEnter->setText("进入聊天室");
}
void TcpClient::on_Send_clicked()
{
if(ui->lineEditSend->text() == "")
{
return;
}
QString msg = userName + ":" + ui->lineEditSend->text();
tcpsocket->write(msg.toUtf8().data());
ui->lineEditSend->clear();
}
void TcpClient::on_pushButtonEnter_clicked()
{
//首先判断这个用户是不是在聊天室中
if(status == false)
{
//不在聊天室中就和服务器进行连接
//从界面获取ip地址
QString ip = ui->lineEditServerIp->text();
//用这个函数判断IP地址是否可以被正确解析
if(!serverIP->setAddress(ip))
{
//不能被正确解析就弹出一个警告窗口
QMessageBox::warning(this, "错误", "IP地址不正确");
return;
}
if(ui->lineEditUserName->text() == "")
{
//用户名不能为空,弹窗效果
QMessageBox::warning(this, "错误", "用户名不能为空");
return;
}
//从界面获取用户名
userName = ui->lineEditUserName->text();
//创建一个通信套接字,用来和服务器进行通信
tcpsocket = new QTcpSocket(this);
//和服务器进行连接
tcpsocket->connectToHost(*serverIP, port);
//和服务器连接成功能会触发connected信号
connect(tcpsocket, SIGNAL(connected()), this, SLOT(slotconnectedsuccess()));
//接收到服务器的信息就会触发readyRead信号
connect(tcpsocket, SIGNAL(readyRead()), this, SLOT(slotreceive()));
//将进入聊天室的标志位置为true;
status = true;
}
else//已经进入了聊天室
{
int length = 0;
QString msg = userName + ":Leave Chat Room";
//离开之前给服务器发送离开信息
tcpsocket->write(msg.toUtf8().data());
tcpsocket->disconnectFromHost();
status = false;
//离开聊天室就会触发disconnected信号
connect(tcpsocket, SIGNAL(disconnected()), this, SLOT(slotdisconnected()));
}
}
#include
#include "tcpclient.h"
#include
int main(int argc, char *argv[])
{
//让中文字符不乱的固定套路
QTextCodec* code = QTextCodec::codecForName("ut f-8");
QTextCodec::setCodecForTr(code);
QTextCodec::setCodecForLocale(code);
QTextCodec::setCodecForCStrings(code);
QApplication a(argc, argv);
TcpClient w;
w.show();
return a.exec();
}
这里说明一下,网上很多例子会把qt中生成的代码贴出来,只要自己创建就好了,没必要贴出来,主函数贴出来是是因为给出了解决中文字符乱码的小套路,大家可以学习一下。
三、服务端程序
服务端分为三个类,我注释写的挺详细了,看不懂评论解答
#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H
#include
//此类为一个继承了Qtcpsocket的类,为多客户端服务
class TcpClientSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit TcpClientSocket(QObject *parent = 0);
signals:
void updateserver(QString, int);//用来告诉tcpserver需要跟新界面的显示
void clientdisconnected(int); //告诉server有客户端断开连接
protected slots:
void receivedata();//处理readyRead信号读取数据
void slotclientdisconnected();//客户端断开连接触发disconnected信号,这个槽函数用来处理这个信号
};
#endif // TCPCLIENTSOCKET_H
#include "tcpclientsocket.h"
#include
TcpClientSocket::TcpClientSocket(QObject *parent)
{
//客户端发送数据过来就会触发readyRead信号
connect(this, SIGNAL(readyRead()), this, SLOT(receivedata()));
connect(this, SIGNAL(disconnected()), this, SLOT(slotclientdisconnected()));
}
void TcpClientSocket::receivedata()
{
int length = 10;
//读取客户端发送的信息并且发送信号给server端
QByteArray array = readAll();
QString msg = array;
emit updateserver(msg,length);
}
void TcpClientSocket::slotclientdisconnected()
{
emit clientdisconnected(this->socketDescriptor());
}
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
#include "tcpclientsocket.h"
class Server : public QTcpServer
{
Q_OBJECT
public:
Server(QObject *parent = 0,int port=0);
QList tcpclientsocketlist;
protected:
void incomingConnection(int socketDescriptor);//只要出现一个新的连接,就会自动调用这个函数
protected slots:
void slotupdateserver(QString, int);//用来处理tcpclient发过来的信号
void slotclientdisconnect(int);
signals:
void updateserver(QString, int);//发送信号给界面,让界面更新信息
};
#endif // SERVER_H
#include "server.h"
#include
#include
Server::Server(QObject *parent, int port):QTcpServer(parent)
{
listen(QHostAddress::Any, port); //监听
}
//当有新的连接出现时就会调用此函数,和newConnection功能类似
void Server::incomingConnection(int socketDescriptor)
{
TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);//只要有新的连接就生成一个新的通信套接字
//将新创建的通信套接字描述符指定为参数socketdescriptor
tcpclientsocket->setSocketDescriptor(socketDescriptor);
//将这个套接字加入客户端套接字列表中
tcpclientsocketlist.append(tcpclientsocket);
//接收到tcpclientsocket发送过来的更新界面的信号
connect(tcpclientsocket, SIGNAL(updateserver(QString,int)), this, SLOT(slotupdateserver(QString,int)));
connect(tcpclientsocket, SIGNAL(clientdisconnected(int)), this, SLOT(slotclientdisconnect(int)));
}
void Server::slotupdateserver(QString msg, int length)
{
//将这个信号发送给界面
emit updateserver(msg, length);
//将收到的信息发送给每个客户端,从套接字列表中找到需要接收的套接字
for(int i = 0; i < tcpclientsocketlist.count(); i++)
{
QTcpSocket *item = tcpclientsocketlist.at(i);
item->write(msg.toUtf8().data());
}
}
void Server::slotclientdisconnect(int descriptor)
{
for(int i = 0; i < tcpclientsocketlist.count(); i++)
{
QTcpSocket *item = tcpclientsocketlist.at(i);
if(item->socketDescriptor() == descriptor)
{
//如果有客户端断开连接, 就将列表中的套接字删除
tcpclientsocketlist.removeAt(i);
return;
}
}
return;
}
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include
#include"server.h"
#include
namespace Ui {
class Tcpserver;
}
class Tcpserver : public QWidget
{
Q_OBJECT
public:
explicit Tcpserver(QWidget *parent = 0);
~Tcpserver();
private:
Ui::Tcpserver *ui;
int port;
Server* server;
public slots:
void slotupdateserver(QString, int);//接收到server发过来的信号就更新界面的信息
private slots:
void on_createbutoon_clicked();
};
#endif // TCPSERVER_H
#include "tcpserver.h"
#include "ui_tcpserver.h"
#include
#include
Tcpserver::Tcpserver(QWidget *parent) :
QWidget(parent),
ui(new Ui::Tcpserver)
{
ui->setupUi(this);
port=5555;
ui->portline->setText(QString::number(port));
server = new Server(this, port);
connect(server, SIGNAL(updateserver(QString, int)), this, SLOT(slotupdateserver(QString, int)));
// ui->pushButtonCreateChattingRoom->setEnabled(false);
}
Tcpserver::~Tcpserver()
{
delete ui;
}
void Tcpserver::slotupdateserver(QString msg, int length)
{
ui->textEdit->append(msg);
}
void Tcpserver::on_createbutoon_clicked()
{
server = new Server(this, port);
connect(server, SIGNAL(updateserver(QString,int)) ,this, SLOT(slotupdateserver(QString,int)));
ui->createbutoon->setEnabled(false);
}
#include
#include "tcpserver.h"
#include
int main(int argc, char *argv[])
{
QTextCodec* code = QTextCodec::codecForName("ut f-8");
QTextCodec::setCodecForTr(code);
QTextCodec::setCodecForLocale(code);
QTextCodec::setCodecForCStrings(code);
QApplication a(argc, argv);
Tcpserver w;
w.show();
return a.exec();
}
这里主要用到很多的信号和槽函数,一层套着一层,从最上面看到下面就好了。有些只是控件名,在Qt中统一一下即可。