目录
TCP/IP协议介绍
网络聊天程序的功能介绍
QT中socket编程
·服务器实现
·客户端实现
测试结果
TCP/IP协议介绍
TCP/IP协议包含的范围非常的广,它是一种四层协议,包含了各种硬件、软件需求的定义。TCP/IP协议确切的说法应该是TCP/UDP/IP协议。UDP协议(User Datagram Protocol 用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输。TCP协议(Transmission Control Protocol 传输控制协议),是一种流传输的协议。他提供可靠的、有序的、双向的、面向连接的传输。
保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。
而面向流则是指无保护消息边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。
举例来说,假如,我们连续发送三个数据包,大小分别是2k、4k、8k,这三个数据包都已经到达了接收端的网络堆栈中,如果使用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有三次接收动作,才能够把所有的数据包接收完。而使用TCP协议,我们只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的数据包接收下来,只需要有一次接收动作。
这就是因为UDP协议的保护消息边界使得每一个消息都是独立的。而流传输,却把数据当作一串数据流,它不认为数据是一个一个的消息。所以有很多人在使用TCP协议通讯的时候,并不清楚TCP是基于流的传输,当连续发送数据的时候,他们时常会认为TCP会丢包。其实不然,因为当它们使用的缓冲区足够大时,它们有可能会一次接收到两个甚至更多的数据包,而很多人往往会忽视这一点,只解析检查了第一个数据包,而已经接收的其它据包却被忽略了。
网络聊天程序的功能介绍
对于此程序的功能和界面在这进行一个简单的说明。首先服务器功能包括打开服务器(监听IP和端口)、接收显示客户端发送过来的消息、与客户端进行通信、在没有客户端接入的时候通过连接信号槽可以的得知且发送按钮失效。其次服务器功能包括通过输入IP地址和端口号进行连接服务器、接受显示服务器发送过来的消息、与服务器进行通信、同样在没有成功连接服务器的时候通过连接信号槽可以的得知且发送按钮失效。
此程序是一对一通信,如果需要进行多客户端通信的话,只需要多开几个线程进行监听即可。
QT中socket编程
·服务器实现
服务器端编程的步骤:
1:创建QT封装类TCP服务器对象server = new QTcpServer();
2:使用server对象调用函数listen()绑定监听IP地址和端口号;
3:建立连接信号槽connect()是否有客户端连接,有且获取客户端信息比如端口号;
4:请求到来后,接受连接请求,socket对象可以与客户端进行通信;
5:返回,等待另一连接请求;
7:关闭server和socket。
mainwindow.ui
MainWindow
0
0
550
583
服务器
110
30
72
31
端口号:
190
30
113
31
340
30
101
31
OpenServer
40
360
451
101
40
130
451
151
400
480
93
28
发送
40
80
91
41
Agency FB
12
消息内容:
40
310
221
41
Agency FB
12
服务器返回消息编辑:
0
0
550
26
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_Listen_clicked();
void on_pushButton_Send_clicked();
void server_New_Connect();
void socket_Read_Data();
void socket_Disconnected();
private:
Ui::MainWindow *ui;
QTcpServer* server;
QTcpSocket* socket;
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QMessageBox"
#include "QTextCodec.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setFixedSize(550,583); // 禁止改变窗口大小。
ui->textEdit_Recv->setReadOnly(true);
ui->lineEdit_Port->setText("6000");
ui->pushButton_Send->setEnabled(false);
server = new QTcpServer();
//连接信号槽
connect(server,&QTcpServer::newConnection,this,&MainWindow::server_New_Connect);
}
MainWindow::~MainWindow()
{
server->close();
server->deleteLater();
delete ui;
}
void MainWindow::on_pushButton_Listen_clicked()
{
ui->textEdit_Recv->setText("");
if(ui->pushButton_Listen->text() == tr("OpenServer"))
{
server = new QTcpServer();
//连接信号槽
connect(server,&QTcpServer::newConnection,this,&MainWindow::server_New_Connect);
ui->textEdit_Recv->insertPlainText("OpenSuccessfully\n");
//从输入框获取端口号
int port = ui->lineEdit_Port->text().toInt();
//监听指定的端口
if(!server->listen(QHostAddress::Any, port))
{
//若出错,则输出错误信息
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
QMessageBox::information(this,tr("消息"),tr("打开服务器失败!"),QMessageBox::Yes);
return;
}
//修改按键文字
ui->pushButton_Listen->setText("CloseServer");
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
QMessageBox::information(this,tr("消息"),tr("打开服务器成功!"),QMessageBox::Yes);
}
else
{
server->deleteLater();
//取消侦听
server->close();
//修改按键文字
ui->pushButton_Listen->setText("OpenServer");
//发送按键失能
ui->pushButton_Send->setEnabled(false);
ui->textEdit_Recv->insertPlainText("CloseSuccessfully\n");
}
}
void MainWindow::on_pushButton_Send_clicked()
{
ui->textEdit_Recv->insertPlainText(tr("服务器:")+ui->textEdit_Send->toPlainText()+"\n");
socket->write(ui->textEdit_Send->toPlainText().toLocal8Bit());
socket->flush();
ui->textEdit_Send->setText("");
}
void MainWindow::server_New_Connect()
{
//获取客户端连接
socket = server->nextPendingConnection();
//连接QTcpSocket的信号槽,以读取新数据
QObject::connect(socket, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
QObject::connect(socket, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
//发送按键使能
ui->pushButton_Send->setEnabled(true);
}
void MainWindow::socket_Read_Data()
{
QByteArray buffer;
//读取缓冲区数据
buffer = socket->readAll();
if(!buffer.isEmpty())
{
QString str = QString::fromLocal8Bit(buffer);
ui->textEdit_Recv->insertPlainText("李青说:"+str+"\n");
}
}
void MainWindow::socket_Disconnected()
{
//发送按键失能
ui->pushButton_Send->setEnabled(false);
}
·客户端实现
客户端编程的步骤:
1:创建QT封装TCP套接字对象socket=new QTcpSocket();
2:建立连接信号槽connect()是否与服务器有连接;
3:向服务器发出连接请求( socket->connectToHost(IP, port));
4:和服务器端进行通信( socket->write());
5:关闭套接字。
mainwindow.ui
MainWindow
0
0
642
699
客户端
60
50
61
41
IP地址:
280
50
61
41
端口号:
120
60
151
21
340
60
113
21
480
50
93
31
连接
490
610
93
28
发送
60
150
521
261
60
490
521
101
60
110
131
31
Agency FB
12
聊天内容:
60
430
121
51
Agency FB
12
编辑消息:
0
0
642
26
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_Connect_clicked();
void on_pushButton_Send_clicked();
void socket_Read_Data();
void socket_Disconnected();
private:
Ui::MainWindow *ui;
QTcpSocket *socket;
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QTcpSocket"
#include "QMessageBox"
#include "QTextCodec.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setFixedSize(642,700); // 禁止改变窗口大小。
socket=new QTcpSocket();
//连接信号槽
QObject::connect(socket, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
QObject::connect(socket, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
ui->textEdit_Recv->setReadOnly(true);
ui->pushButton_Send->setEnabled(false);
ui->lineEdit_IP->setText("127.0.0.1");
ui->lineEdit_Port->setText("6000");
}
MainWindow::~MainWindow()
{
delete this->socket;
delete ui;
}
void MainWindow::on_pushButton_Connect_clicked()
{
if(ui->pushButton_Connect->text() == tr("连接"))
{
QString IP;
int port;
//获取IP地址
IP = ui->lineEdit_IP->text();
//获取端口号
port = ui->lineEdit_Port->text().toInt();
ui->textEdit_Recv->setText("");
ui->textEdit_Recv->insertPlainText("正在连接"+ui->lineEdit_IP->text()+":"+ui->lineEdit_Port->text()+"\n");
//取消已有的连接
socket->abort();
//连接服务器
socket->connectToHost(IP, port);
//等待连接成功
if(!socket->waitForConnected(30000))
{
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
QMessageBox::warning(this,tr("消息"),tr("连接失败!请重新连接"),QMessageBox::Yes);
ui->textEdit_Recv->insertPlainText("连接失败\n");
return;
}
ui->textEdit_Recv->insertPlainText("连接成功\n");
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
QMessageBox::information(this,tr("消息"),tr("连接成功"),QMessageBox::Yes);
//发送按键使能
ui->pushButton_Send->setEnabled(true);
//修改按键文字
ui->pushButton_Connect->setText("断开连接");
}
else
{
//断开连接
ui->textEdit_Recv->setText("断开连接\n");
socket->disconnectFromHost();
//修改按键文字
ui->pushButton_Connect->setText("连接");
ui->pushButton_Send->setEnabled(false);
}
}
void MainWindow::on_pushButton_Send_clicked()
{
ui->textEdit_Recv->insertPlainText(tr("李青说:")+ui->textEdit_Send->toPlainText()+"\n");
socket->write(ui->textEdit_Send->toPlainText().toLocal8Bit());
socket->flush();
ui->textEdit_Send->setText("");
}
void MainWindow::socket_Read_Data()
{
QByteArray buffer;
//读取缓冲区数据
buffer = socket->readAll();
if(!buffer.isEmpty())
{
QString str = QString::fromLocal8Bit(buffer);
ui->textEdit_Recv->insertPlainText("服务器消息:"+str+"\n");
}
}
void MainWindow::socket_Disconnected()
{
//发送按键失能
ui->pushButton_Send->setEnabled(false);
//修改按键文字
ui->pushButton_Connect->setText("连接");
qDebug() << "Disconnected!";
}
测试结果