一、linux下的tcp通信过程
其中bind绑定,会固定一个端口,否则是随机的。
一个链接是由双方的ip和端口组成的,固定端口保证源的不变性,
这样另一端在任何时候访问的目的都是一致的,也可以说这个端口提供了什么服务。
同时绑定后直接操作socket id就可以操作对应的链接了。
二、QT下的TCP通信过程
Qt中提供的所有的Socket类都是非阻塞的。
Qt中常用的用于socket通信的套接字类:
QTcpServer
用于TCP/IP通信, 作为服务器端套接字使用
QTcpSocket
用于TCP/IP通信,作为客户端套接字使用。
QUdpSocket
用于UDP通信,服务器,客户端均使用此套接字。
1.QT下的服务端
1).socket函数变为QTcpServer
2).bind ,listen 统一为listen
同时没有accept,当有一个链接过来的时候,会产生一个信号:newconnection,可以从对应的槽函数中取出建立好的套接字(对方的)QTcpSocket 如果成功和对方建立好链接,通信套接字会自动触发connected信号
3).read :
对方发送数据过来,链接的套接字(通信套接字)就会触发(本机的)readyRead信号,需要在对应的槽函数中接收数据
4).write,
发送数据,对方的(客户端的)套接字(通信套接字)就会触发readyRead信号,需要在对应的槽函数中接收数据 如果对方主动断开连接,对方的(客户端的)套接字(通信套接字)会自动触发disconnected信号
2.QT下的客户端:
1).socket函数变为 QTcpSocket
2).connect变为connetToHost()
如果成功和对方建立好链接,就会自动触发connected信号
3).read :
对方发送数据过来,链接的套接字(通信套接字)就会触发(本机的)readyRead信号,需要在对应的槽函数中接收数据
4).write,
发送数据,对方的(服务器的)套接字(通信套接字)就会触发readyRead信号,需要在对应的槽函数中接收数据,如果对方主动断开连接,就会自动触发disconnected信号
具体见图《QtTCP通信过程》
三、TCP服务器
Qwidget是基类,比较干净,QMainWindow相对比较多。
如果输入头文件没有提示,就需要在项目文件中加入对应模块,同时再编译不运行一下,让qt可以构建并加载对应的模块。
#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字//对方的(客户端的)套接字(通信套接字)
//监听套接字,指定父对象,让其自动回收空间
tcpServer = new QTcpServer(this);
tcpServer->listen(QHostAddress::Any, 8888);
setWindowTitle("服务器: 8888");
connect(tcpServer, &QTcpServer::newConnection,
[=]()//信号无参数,这里也没有参数
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的IP和端口
QString ip = tcpSocket->peerAddress().toString();
qint16 port = tcpSocket->peerPort();
QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
ui->textEditRead->setText(temp);
//必须放在里面,因为建立好链接才能读,或者说tcpSocket有指向才能操作
connect(tcpSocket, &QTcpSocket::readyRead,
[=]()
{
//从通信套接字中取出内容
QByteArray array = tcpSocket->readAll();
ui->textEditRead->append(array);
}
);
}
);
void ServerWidget::on_buttonSend_clicked()
{
if(NULL == tcpSocket)
{
return;
}
//获取编辑区内容
QString str = ui->textEditWrite->toPlainText();
//给对方发送数据, 使用套接字是tcpSocket
tcpSocket->write( str.toUtf8().data() );
}
void ServerWidget::on_buttonClose_clicked()
{
if(NULL == tcpSocket)
{
return;
}
//主动和客户端断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
tcpSocket = NULL;
}
四、TCP客户端
可以在项目中添加新文件中选择Qt--->Qt设计师界面类(这个是带ui的),选择这个后项目会多出一个ui
ui->setupUi(this);//显示ui
tcpSocket = NULL;
//分配空间,指定父对象
tcpSocket = new QTcpSocket(this);
setWindowTitle("客户端");
connect(tcpSocket, &QTcpSocket::connected,
[=]()
{
ui->textEditRead->setText("成功和服务器建立好连接");
}
);
//因为tcpSocket已经分配了空间,有指向,所以可以放在外面
connect(tcpSocket, &QTcpSocket::readyRead,
[=]()
{
//获取对方发送的内容
QByteArray array = tcpSocket->readAll();
//追加到编辑区中
ui->textEditRead->append(array);
}
);
void ClientWidget::on_buttonConnect_clicked()
{
//获取服务器ip和端口
QString ip = ui->lineEditIP->text();
qint16 port = ui->lineEditPort->text().toInt();
//主动和服务器建立连接
tcpSocket->connectToHost(QHostAddress(ip), port);
}
void ClientWidget::on_buttonSend_clicked()
{
//获取编辑框内容
QString str = ui->textEditWrite->toPlainText();
//发送数据
tcpSocket->write( str.toUtf8().data() );
}
void ClientWidget::on_buttonClose_clicked()
{
//主动和对方断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();//这里释放连接,前面connect的时候会建立连接
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ServerWidget w;
w.show();
ClientWidget w2;
w2.show();//显示另外一个窗口
return a.exec();
}
上述代码具体见《TCP》
serverwidget.h
1 #ifndef SERVERWIDGET_H
2 #define SERVERWIDGET_H
3
4 #include <QWidget>
5 #include <QTcpServer> //监听套接字
6 #include <QTcpSocket> //通信套接字
7
8 namespace Ui {
9 class ServerWidget;
10 }
11
12 class ServerWidget : public QWidget
13 {
14 Q_OBJECT
15
16 public:
17 explicit ServerWidget(QWidget *parent = 0);
18 ~ServerWidget();
19
20 private slots:
21 void on_buttonSend_clicked();
22
23 void on_buttonClose_clicked();
24
25 private:
26 Ui::ServerWidget *ui;
27
28 QTcpServer *tcpServer; //监听套接字
29 QTcpSocket *tcpSocket; //通信套接字
30
31 };
32
33 #endif // SERVERWIDGET_H
serverwidget.cpp
1 #include "serverwidget.h"
2 #include "ui_serverwidget.h"
3
4 ServerWidget::ServerWidget(QWidget *parent) :
5 QWidget(parent),
6 ui(new Ui::ServerWidget)
7 {
8 ui->setupUi(this);
9
10 tcpServer = NULL;
11 tcpSocket = NULL;
12
13 //监听套接字,指定父对象,让其自动回收空间
14 tcpServer = new QTcpServer(this);
15
16 tcpServer->listen(QHostAddress::Any, 8888);
17
18 setWindowTitle("服务器: 8888");
19
20 connect(tcpServer, &QTcpServer::newConnection,
21 [=]()
22 {
23 //取出建立好连接的套接字
24 tcpSocket = tcpServer->nextPendingConnection();
25
26 //获取对方的IP和端口
27 QString ip = tcpSocket->peerAddress().toString();
28 qint16 port = tcpSocket->peerPort();
29 QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
30
31 ui->textEditRead->setText(temp);
32
33 connect(tcpSocket, &QTcpSocket::readyRead,
34 [=]()
35 {
36 //从通信套接字中取出内容
37 QByteArray array = tcpSocket->readAll();
38 ui->textEditRead->append(array);
39 }
40
41 );
42
43
44 }
45
46 );
47
48 }
49
50 ServerWidget::~ServerWidget()
51 {
52 delete ui;
53 }
54
55 void ServerWidget::on_buttonSend_clicked()
56 {
57 if(NULL == tcpSocket)
58 {
59 return;
60 }
61 //获取编辑区内容
62 QString str = ui->textEditWrite->toPlainText();
63 //给对方发送数据, 使用套接字是tcpSocket
64 tcpSocket->write( str.toUtf8().data() );
65
66 }
67
68 void ServerWidget::on_buttonClose_clicked()
69 {
70 if(NULL == tcpSocket)
71 {
72 return;
73 }
74
75 //主动和客户端端口连接
76 tcpSocket->disconnectFromHost();
77 tcpSocket->close();
78 tcpSocket = NULL;
79 }
clientwidget.h
1 #ifndef CLIENTWIDGET_H
2 #define CLIENTWIDGET_H
3
4 #include <QWidget>
5 #include <QTcpSocket> //通信套接字
6
7 namespace Ui {
8 class ClientWidget;
9 }
10
11 class ClientWidget : public QWidget
12 {
13 Q_OBJECT
14
15 public:
16 explicit ClientWidget(QWidget *parent = 0);
17 ~ClientWidget();
18
19 private slots:
20 void on_buttonConnect_clicked();
21
22 void on_buttonSend_clicked();
23
24 void on_buttonClose_clicked();
25
26 private:
27 Ui::ClientWidget *ui;
28
29 QTcpSocket *tcpSocket; //通信套接字
30 };
31
32 #endif // CLIENTWIDGET_H
clientwidget.cpp
1 #include "clientwidget.h"
2 #include "ui_clientwidget.h"
3 #include <QHostAddress>
4
5 ClientWidget::ClientWidget(QWidget *parent) :
6 QWidget(parent),
7 ui(new Ui::ClientWidget)
8 {
9 ui->setupUi(this);
10
11 tcpSocket = NULL;
12
13 //分配空间,指定父对象
14 tcpSocket = new QTcpSocket(this);
15
16 setWindowTitle("客户端");
17
18
19 connect(tcpSocket, &QTcpSocket::connected,
20 [=]()
21 {
22 ui->textEditRead->setText("成功和服务器建立好连接");
23 }
24 );
25
26 connect(tcpSocket, &QTcpSocket::readyRead,
27 [=]()
28 {
29 //获取对方发送的内容
30 QByteArray array = tcpSocket->readAll();
31 //追加到编辑区中
32 ui->textEditRead->append(array);
33 }
34
35 );
36
37 }
38
39 ClientWidget::~ClientWidget()
40 {
41 delete ui;
42 }
43
44 void ClientWidget::on_buttonConnect_clicked()
45 {
46 //获取服务器ip和端口
47 QString ip = ui->lineEditIP->text();
48 qint16 port = ui->lineEditPort->text().toInt();
49
50 //主动和服务器建立连接
51 tcpSocket->connectToHost(QHostAddress(ip), port);
52
53 }
54
55 void ClientWidget::on_buttonSend_clicked()
56 {
57 //获取编辑框内容
58 QString str = ui->textEditWrite->toPlainText();
59 //发送数据
60 tcpSocket->write( str.toUtf8().data() );
61
62 }
63
64 void ClientWidget::on_buttonClose_clicked()
65 {
66 //主动和对方断开连接
67 tcpSocket->disconnectFromHost();
68 tcpSocket->close();
69 }
五、UDP通信过程
使用Qt提供的QUdpSocket进行UDP通信。在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发送数据。
类似的服务器也不从客户端接收连接,只负责调用接收函数,等待来自客户端的数据的到达。
在UDP通信中,服务器端和客户端的概念已经显得有些淡化,两部分做的工作都大致相同
1.QT下的服务端
socket函数变为QUdpSocket
bind ,还是bind,(固定端口,让别人可以知道往哪里发,客户端也可以绑定)
readDatagram :
对方发送数据过来,套接字就会触发readyRead信号,需要在对应的槽函数中接收数据
writeDatagram,
发送数据,对方的(客户端的)套接字就会触发readyRead信号,需要在对应的槽函数中接收数据
close 还是close
2.QT下的客户端:
socket函数变为 QUdpSocket
readDatagram :对方发送数据过来,套接字就会触发readyRead信号,需要在对应的槽函数中接收数据writeDatagram,发送数据,对方的(客户端的)套接字就会触发readyRead信号,需要在对应的槽函数中接收数据
close 还是close
六、UDP文本发送
UDP中没有严格的区分服务端和客户端。
关闭按钮是用于关闭窗口的,这主要是由于udp不是面向连接的,没有断开连接的说法。
#include <QUdpSocket> //UDP套接字
//分配空间,指定父对象,这是为了让父对象来回收,其实也可以不用指定,自己来回收资源也行
udpSocket = new QUdpSocket(this);
//绑定
udpSocket->bind(8888);
setWindowTitle("服务器端口为:8888");
//当对方成功发送数据过来
//自动触发 readyRead()
connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::dealMsg);
void Widget::dealMsg()
{
//读取对方发送的内容
char buf[1024] = {0};
QHostAddress cliAddr; //对方地址
quint16 port; //对方端口
qint64 len = udpSocket->readDatagram(buf, sizeof(buf), &cliAddr, &port);
if(len > 0)
{
//格式化 [192.68.2.2:8888]aaaa
QString str = QString("[%1:%2] %3")
.arg(cliAddr.toString())
.arg(port)
.arg(buf);
//给编辑区设置内容
ui->textEdit->setText(str);
}
}
//发送按钮
void Widget::on_buttonSend_clicked()
{
//先获取对方的IP和端口
QString ip = ui->lineEditIP->text();
qint16 port = ui->lineEditPort->text().toInt();
//获取编辑区内容
QString str = ui->textEdit->toPlainText();
//给指定的IP发送数据
udpSocket->writeDatagram(str.toUtf8(), QHostAddress(ip), port);
}