Based on C++ and Qt package a simple socket (TCP/IP) communication UI interface

        Recently, I am learning about TCP/IP and socket sockets. I have learned a lot of knowledge about three-way handshake and four-way wave, TCP protocol, etc., but I feel shallow on paper. There are a lot of information on C++ code to realize socket communication on the Internet, which is convenient for learning, so I thought of using Qt to implement a basic server-side UI software with network communication sending and receiving functions. Into the title:

1. UI interface and function introduction

        Here we program under the Windows system , use the Qt5 framework, use the button (pushButton) to initialize the socket and click to send information, and the window for receiving information and sending information is implemented using the text edit box (textEdit). There are only two buttons and two text boxes in the entire interface, and the functions realized are very simple, that is, as a server in TCP/IP communication, waiting for the client to connect, and communicating with the client. The entire UI interface is as follows:

Software process:

        1. Click the initialization button, call functions such as bind and listen to initialize the socket, and call accept to wait for the client to connect;

        2. While calling accept, a pop-up dialog box (messageBox) reminds that it is waiting for the client to connect;

        3.  The client connects to the server, the accept is completed, and the dialog box is automatically closed;

        4.  Enter characters in the sending window and click Send to transmit, and receive messages from the client in the receiving window.

In addition, when the server fails to send a message, it is considered that the client has disconnected the socket connection, so the socket used for connection and current communication is closed, the initial state is restored, and the next connection can be made.

2. Code implementation

        There are many comments in this part, and the code will not be explained here. The points that need attention are:

        First of all, the main thread of Qt is the UI thread, so if you put a long time-consuming operation on the main thread, it will affect the drawing and refreshing of the UI interface, causing the interface to be blocked, and the software cannot be used normally. Therefore, waiting for the client to connect and receiving messages here are opened separately to run the thread ( std::thread ), of course, you can also use QThread for multi-threaded operations.

        Secondly, send the custom signal acceptStart() before the accept block to make the message prompt box pop up; after the connection is completed, send another custom signal acceptFinish() again to close the message prompt box.

        Finally, if you want to implement the socket reconnection function, you must first close (closesocket) the invalid (disconnected) socket, and then initialize it again, otherwise you cannot connect.

Server.h

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_Server.h"
#include <errno.h>
#include <sys/types.h>
#include <thread>
#include <stdio.h>
#include <iostream>
#include<WinSock2.h>
#include<QMessageBox>

#pragma comment(lib,"ws2_32.lib")

class Server : public QMainWindow
{
    Q_OBJECT

public slots:
	bool init_server();
	void send_msg();

signals:
	void acceptStart();
	void acceptFinish();

public:
    Server(QWidget *parent = nullptr);
    ~Server();
	void recv_msg();
	//接收信息线程
	void recv_thread_function();
	//初始化socket线程
	void init_server_thread_function();

private:
    Ui::ServerClass ui;
	static const int BUF_SIZE = 0XFFFF;
	//接收线程和初始化线程
	std::thread *recv_thread, *init_thread;
	//用于连接和用于通信的socket
	int socket_fd, client_fd;
	//ip地址和端口
	std::string server_ip;
	unsigned short server_port;
	//服务端是否工作标志位
	bool isServerWork;
	//接收和发送缓冲区
	char recv_buf[BUF_SIZE];
	char send_buf[BUF_SIZE];
	//开始通信过程
	void start();
	//关闭socket
	void on_disconnected();
	//等待连接时弹出的对话框
	QMessageBox* dialogWait;
};

Server.cpp

#include "Server.h"

Server::Server(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
	//初始化用于连接的socket
	socket_fd = -1;
	server_ip = "127.0.0.1";
	server_port = 8888;
	isServerWork = true;
	dialogWait = new QMessageBox(this);
	dialogWait->setText(QStringLiteral("正在等待客户端连接..."));
	dialogWait->addButton(QMessageBox::Ok);
	connect(ui.pushButtonInit, &QPushButton::clicked, this, &Server::init_server);
	connect(ui.pushButtonSend, &QPushButton::clicked, this, &Server::send_msg);
	connect(this, &Server::acceptStart, this->dialogWait, &QMessageBox::show);
	connect(this, &Server::acceptFinish, this->dialogWait, &QMessageBox::accept);
}

Server::~Server()
{}

void Server::init_server_thread_function()
{
	struct sockaddr_in client;
	int len = sizeof(client);
	if (isServerWork)
	{
		emit acceptStart();//弹出等待对话框
		client_fd = accept(socket_fd, (struct sockaddr*)&client, &len);//此处阻塞直到客户端连接
		if (client_fd == -1)
		{
			perror("accept");
			exit(-1);
		}
		emit acceptFinish();//关闭等待对话框
		char *client_ip = inet_ntoa(client.sin_addr);
		ui.textEditRecv->append(QStringLiteral("连接成功,客户端IP为:"));
		ui.textEditRecv->append(client_ip);
		//服务端和客户端连接后可以开启接收线程
		recv_thread = new std::thread(std::bind(&Server::recv_thread_function, this));
	}
}

bool Server::init_server()
{
	//win系统下必须进行WSAStartup
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(1, 1);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return S_FALSE;
	}
	std::string ip = server_ip;
	unsigned short port = server_port;
	//不进行WSAStartup,socket_fd无法创建成功
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (socket_fd == -1)
	{
		perror("socket");
		exit(-1);
	}
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ip.c_str());

	int ret = bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr));
	if (ret == -1)
	{
		perror("bind");
		exit(-1);
	}

	listen(socket_fd, 10);
	ui.textEditRecv->append(QStringLiteral("等待客户端连接..."));
	start();
	return true;
}

void Server::start()
{
	ui.pushButtonInit->setEnabled(false);
	//等待accept会阻塞UI,此处开线程进行处理
	init_thread = new std::thread(std::bind(&Server::init_server_thread_function, this));
}

void Server::send_msg()
{
	if (ui.textEditSend->document()->isEmpty())
		return;
	QString qstrToSend = ui.textEditSend->toPlainText();
	QByteArray ba = qstrToSend.toLatin1();
	char* buf = ba.data();
	memset(send_buf, 0, BUF_SIZE);
	memcpy(send_buf, buf, sizeof(buf));
	int ret = send(client_fd, send_buf, strlen(send_buf), 0);
	//若发送失败,考虑已经断线,可以关闭socket并进行下一步重连
	if (ret < 0)
	{
		on_disconnected();
		ui.textEditRecv->append(QStringLiteral("连接已断开,请重新连接!"));
		ui.pushButtonInit->setEnabled(true);
	}
		
}

void Server::recv_msg()
{
	int recv_size = recv(client_fd, recv_buf, sizeof(recv_buf), 0);
	if (recv_size <= 0)
		return;
	char recv_content[1024];
	memset(recv_content, 0, sizeof(recv_content));
	memcpy(&recv_content, recv_buf, sizeof(recv_content));
	ui.textEditRecv->append(recv_content);
}

void Server::on_disconnected()
{
	closesocket(socket_fd);
	closesocket(client_fd);
}

void Server::recv_thread_function()
{
	while (true)
	{
		recv_msg();
	}
}

3. Software effect

        

         The network debugging assistant used here is the NetAssist software of Savage Homeland, which plays the role of client here. Download link: http://www.cmsoft.cn/download/cmsoft/netassist.zip http://www.cmsoft.cn/download/cmsoft/netassist.zip

         There are still some problems in this software: If the server does not send messages to the client, the socket cannot be automatically closed; the Chinese cannot be sent, etc., which can be improved.

Guess you like

Origin blog.csdn.net/m0_57315535/article/details/129998046