Qt-Day11-学习笔记

Qt的TCP协议

  • 1. Qt中的网络
  • 2. 基本的网络编程基础
    • 2.1. 我们对互联网的理解
    • 2.2. 什么是协议?
    • 2.3. 什么是端口?
  • 3. windows上的TCP编程模型和代码
  • 4. Qt上的TCP编程模型
  • 总结

Qt-Day11-学习笔记_第1张图片

1. Qt中的网络

Qt-Day11-学习笔记_第2张图片

2. 基本的网络编程基础

2.1. 我们对互联网的理解

Qt-Day11-学习笔记_第3张图片

2.2. 什么是协议?

IP:用来区分主机的
ipv4 4个字节来保存 IP地址
指针 指针变量:4个字节来保存内存地址
如:“192.168.0.166” : 4段,每段1个字节,0-255
子网掩码:255.255.255.0

前三段 用来 表示网络号
最后一段 用来 表示哪台主机号

2.3. 什么是端口?

一个操作系统上,通常有65535个端口号
Qt-Day11-学习笔记_第4张图片
端口的作用是用来区分同一台主机上的不同的应用程序.

一般为了区分,一个程序会占用一个或者多个端口号,其他程序不能抢.

自己写代码时.注意:使用1000左右的端口号就行了.
注意:大小端问题,注意:转换和是否需要转换
Qt-Day11-学习笔记_第5张图片

3. windows上的TCP编程模型和代码

Qt-Day11-学习笔记_第6张图片
客户端:

#include 
#pragma comment(lib,"Ws2_32.lib")
#include 
#include 

int main()
{
	//0. 请求协议版本
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		printf("请求协议版本失败:%d\n", GetLastError);
		return -1;
	}
	printf("请求协议版本成功!\n");

	//1. 创建socket
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (SOCKET_ERROR == serverSocket)
	{
		printf("创建socket失败:%d\n", GetLastError);
		//8. 清理协议版本信息
		WSACleanup();
		return -1;
	}
	printf("创建socket成功!\n");

	//2. 服务器协议地址簇
	SOCKADDR_IN sAddr = { 0 };
	sAddr.sin_family = AF_INET;								//和socket第一个参数一致
	sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	//把数字格式字符串转换为网络字节序
	sAddr.sin_port = htons(10086);							//65535 5000以上,大小端转换

															//3. 连接服务器
	int r = connect(serverSocket, (sockaddr*)&sAddr, sizeof sAddr);
	if (-1 == r)
	{
		printf("连接服务器失败:%d\n", GetLastError);
		//7. 关闭socket
		closesocket(serverSocket);
		WSACleanup();
		return -1;
	}
	printf("连接服务器成功!\n");

	//4. 通信
	char buff[1024];
	while (1)
	{
		printf("请输入:");
		scanf("%s", buff);

		send(serverSocket, buff, strlen(buff), NULL);
	}

	//5. 关闭socket
	closesocket(serverSocket);

	//6. 清理协议版本信息
	WSACleanup();

	return 0;
}

服务器端:

#include 
#pragma comment(lib,"Ws2_32.lib")
#include 
#include 

//最多1024个客户端连接	没有管理客户端的socket
SOCKET clientSocket[1024];

int count = 0;

//线程处理函数
void myThreadFunction(void *arg);

int main()
{
	//0 请求协议版本
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (LOBYTE(wsaData.wVersion) != 2 ||
		HIBYTE(wsaData.wVersion) != 2) {
		printf("请求协议版本失败:%d\n", GetLastError());
		return -1;
	}
	printf("请求协议版本成功!\n");

	//1 创建socket
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (SOCKET_ERROR == serverSocket) {
		printf("创建socket失败:%d\n", GetLastError());
		//8 清理协议版本信息
		WSACleanup();
		return -1;
	}
	printf("创建socket成功!\n");

	//2 服务器协议地址簇
	SOCKADDR_IN sAddr = { 0 };
	sAddr.sin_family = AF_INET;								// 和socket第一个参数一致
	sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	// 把数点格式字符串转换成网络字节序
	sAddr.sin_port = htons(10086);							// 65536   5000以上    大小端转换

															//3 绑定
	int r = bind(serverSocket, (sockaddr*)&sAddr, sizeof sAddr);
	if (-1 == r) {
		printf("绑定失败:%d\n", GetLastError());
		//7 关闭socket
		closesocket(serverSocket);
		//8 清理协议版本信息
		WSACleanup();
		return -1;
	}
	printf("绑定成功!\n");

	//4. 监听
	r = listen(serverSocket,10);
	if (-1 == r)
	{
		printf("监听失败:%d\n",GetLastError());
		//7. 关闭socket
		closesocket(serverSocket);
		//8. 清理协议版本信息
		WSACleanup();
		return -1;
	}
	printf("监听成功!\n");

	//5. 等待客户端连接
	SOCKADDR_IN cAddr = { 0 };
	int len = sizeof cAddr;
	for (int i = 0;i < 1024;i++)
	{
		memset(&cAddr,0,len);

		clientSocket[i] = accept(serverSocket,(sockaddr*)&cAddr,&len);	//阻塞
		if (SOCKET_ERROR == clientSocket[i])
		{
			printf("客户端连接失败:%d\n",GetLastError());
			//7. 关闭socket
			closesocket(serverSocket);
			//8. 清理协议版本信息
			WSACleanup();
			return -1;
		}
		printf("有客户端连接到服务器了:%s,%d!\n",
			inet_ntoa(cAddr.sin_addr),ntohl(cAddr.sin_port));

		CreateThread(NULL,NULL,
			(LPTHREAD_START_ROUTINE)myThreadFunction,
			(void*)i,NULL,NULL);
	}

	//7. 关闭socket
	closesocket(serverSocket);
	//8. 清理协议版本信息
	WSACleanup();


	return 0;
}

//线程处理函数
void myThreadFunction(void* arg)
{
	int idx = (int)arg;
	char buff[1024];
	int r;
	while (1)
	{
		r = recv(clientSocket[idx],buff,1023,NULL);
		if (r > 0)
		{
			buff[r] = 0;
			printf("客户端%d发来数据:%s\n",idx,buff);
		}
	}
}

客户端:
Qt-Day11-学习笔记_第7张图片
服务器端:
Qt-Day11-学习笔记_第8张图片

4. Qt上的TCP编程模型

Qt-Day11-学习笔记_第9张图片
服务器:

#include "widget.h"
#include "ui_widget.h"
#pragma execution_character_set("GB2312")

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle("服务器 ");

    //1. 创建QTcpSocket对象
    pTcpServer = new QTcpServer(this);      //指定父对象,让他可以自动回收内存
    pTcpSocket = NULL;

    //2. 监听
    pTcpServer->listen(QHostAddress::Any,10086);

    //3. 等待客户端连接
    //3.1. 关联信号与槽
    //connect(pTcpServer,QTcpServer::newConnection,this,...)
    connect(pTcpServer,&QTcpServer::newConnection,
            [=](){
        //3.2. 获取客户端socket
        pTcpSocket = pTcpServer->nextPendingConnection();
        qDebug() << "客户端连接服务器成功" << endl;
        //3.3. 获取客户端ip地址和端口号
        QString ipStr = pTcpSocket->peerAddress().toString();
        quint16 portUint = pTcpSocket->peerPort();
        //相当于sprintf
        QString buff = QString("客户端ip:%1,客户端端口号:%2").arg(ipStr).arg(portUint);
        //3.4. 显示在某个地方          ui.texteditRecv
        ui->textEdit_Recv->setText(buff);

        //4. 通信
        //4.1. 服务器接受客户端数据
        connect(pTcpSocket,&QTcpSocket::readyRead,
                [=](){
            QByteArray data = pTcpSocket->readAll();
            ui->textEdit_Recv->append(data);
        });


    });

}

Widget::~Widget()
{
    delete ui;
}

        //4.2. 发送数据         服务器 发送到 客户端
void Widget::on_btn_Send_clicked()
{
    if(pTcpSocket)
    {
        QString str = ui->textEdit_Send->toPlainText();
        pTcpSocket->write(str.toUtf8().data());
    }
}

void Widget::on_btn_Close_clicked()
{
    if(pTcpSocket)
    {
        pTcpSocket->disconnectFromHost();
        pTcpSocket->close();
    }
}

客户端:

#include "clientwidget.h"
#include "ui_clientwidget.h"
#pragma execution_character_set("GB2312")

clientwidget::clientwidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::clientwidget)
{
    ui->setupUi(this);

    setWindowTitle("客户端 ");

//1. 创建QTcpSocket对象
    pTcpSocket = new QTcpSocket(this);

    //2.3. 检查是否连接上了服务器
    connect(pTcpSocket,&QTcpSocket::connected,
            [=](){
        ui->textEdit_Recv->setText("连接服务器成功!");
        //3. 通信
            //3.1. 客户端接受服务器数据
            connect(pTcpSocket,&QTcpSocket::readyRead,
                    [=](){
               QByteArray data = pTcpSocket->readAll();
               ui->textEdit_Recv->append(data);
            });
        });

}

clientwidget::~clientwidget()
{
    delete ui;
}

//2. 连接服务器
void clientwidget::on_btn_Connect_clicked()
{
    //2.1. 获取用户输入的ip地址和端口号
    QString ipStr = ui->lineEdit_IP->text();
    quint16 portUint = ui->lineEdit_Port->text().toUInt();
    //2.2. 连接服务器
    pTcpSocket->connectToHost(ipStr,portUint);
}

        //3.2. 客户端 发送数据到 服务器
void clientwidget::on_btn_send_clicked()
{
   if(pTcpSocket)
   {
       QString str = ui->textEdit_Send->toPlainText();
       pTcpSocket->write(str.toUtf8().data());
   }
}

//4. 断开连接
void clientwidget::on_pushButton_3_clicked()
{
    if(pTcpSocket)
    {
        pTcpSocket->disconnectFromHost();
        pTcpSocket->close();
    }
}

Qt-Day11-学习笔记_第10张图片
Qt-Day11-学习笔记_第11张图片

总结

- 1> 解决中文乱码:
Qt-Day11-学习笔记_第12张图片
不限制长度,将其改为:str.toUtf8().data()
Qt-Day11-学习笔记_第13张图片

- 2> 解决"当服务器没有连接客户端时,而从客户端直接发送数据给服务器时的闪退"异常:
Qt-Day11-学习笔记_第14张图片

你可能感兴趣的:(学习笔记,qt)