基于C++的Qt网络编程——聊天客户端

一、实验题目 

网络聊天程序的设计与实现

二、实验目的 

了解Socket通信的原理,在此基础上编写一个聊天程序。

总体设计

1. 基本原理

socket通信原理是一种“打开——读/写——关闭”模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方的内容,通讯结束时关闭文件。

Socket在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。

Socket保证了不同计算机之间的通信,对于网站,通信模型是服务器与客户端之间的通信。两端都建立一个socket对象,然后通过socket对象对数据进行传输。通常服务器处于一个无限循环,等待客户端的连接。

2. 设计步骤

(1)服务器端编程的步骤

①加载套接字库,创建套接字WSAStartup();

在使用socket之前要进行版本的设定和初始化,应用程序只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数。根据版本初始化windows socket,返回0表示成功。

②创建套接字,使用TCP协议;

有套接字的接口才能进行通信。

③绑定套接字到一个 IP 地址和一个端口上(bind());

用bind()函数确定socket各种属性。

④将套接字设置为监听模式等待连接请求(listen());

⑤循环等待请求到来,每接受一个连接请求,返回一个新的对应于此次连接的套接字(accept());

accept()是一个阻塞函数,如果没有客户端请求,连接会一直等待在这里。该函数会返回一个新的套接字,这个新的套接字是用来与客户端通信的套接字,之前那个套接字是用来监听的套接字。

⑥用返回的套接字和客户端进行通信(send()/recv());

⑦关闭套接字,关闭加载的套接字库(closesocket())。

(2)客户端编程的步骤

①加载套接字库,创建套接字WSAStartup();

要连接的服务器的ip,因为现在服务器端就是本机,所以写本机ip,127.0.0.1一个特殊的IP地址,表示是本机的IP地址。

②向服务器发出连接请求(connect());

如果没有成功连接到服务器,一直循环,直至连接上为止。

③和服务器端进行通信(send()/recv());

④关闭套接字,关闭加载的套接字库(closesocket())。

详细设计 

程序流程图

基于C++的Qt网络编程——聊天客户端_第1张图片

 实现代码

客户端

myClient.h

#ifndef MYCLIENT_H
#define MYCLIENT_H

#include 
#include 
#include "winsock2.h"
#include "stdlib.h"
#include "stdio.h"
#include "string"
#include 
#pragma comment (lib, "ws2_32.lib")

QT_BEGIN_NAMESPACE
namespace Ui { class MyClient; }
QT_END_NAMESPACE

class MyClient : public QWidget
{
    Q_OBJECT
public:
    MyClient(QWidget *parent = nullptr);
    ~MyClient();
private slots:
    void on_btcon_clicked();
    void on_btsend_clicked();
    void on_btclose_clicked();
private:
    Ui::MyClient *ui;
};

class WorkThread : public QThread
{
public:
    WorkThread();
protected:
    void run();
};
#endif // MYCLIENT_H

main.cpp

主要是解决中文乱码问题,这里给出解决方案的一种

//添加库
#include 
//设置编码GBK
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));

myClient.cpp

#include "myclient.h"
#include "ui_myclient.h"
Ui::MyClient* mui;

SOCKET serverSocket;//服务器

MyClient::MyClient(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyClient)
{
    ui->setupUi(this);
	mui = ui;
}

void MyClient::on_btcon_clicked()
{
	WorkThread* thread = new WorkThread();
	thread->start();
}

void MyClient::on_btsend_clicked()
{
	char* buff;
	QString str = mui->tesend->toPlainText();
	QByteArray ba = str.toLocal8Bit();
	buff = ba.data();
	int r = send(serverSocket, buff, strlen(buff), NULL);
    //根据r的值查看是否发送成功
}

void recvAndShow()
{
	int r, i = 0;
	char buff[256];
	while (1)
	{
		memset(buff, 0, 256);
		r = recv(serverSocket, buff, 255, NULL);
		if (r > 0)
		{
			mui->tbrecv->append(QString::fromLocal8Bit(buff,256));
			i++;
		}
	}
}

void MyClient::on_btclose_clicked()
{
	closesocket(serverSocket);
	WSACleanup();
	return;
}

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

WorkThread::WorkThread()
{
}

void WorkThread::run()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData) != 0;//成功==0
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)//请求版本失败
	{
		return ;
	}
	//创建socket
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (serverSocket == INVALID_SOCKET)//创建socket失败!
	{
		return ;
	}
	//地址族
	SOCKADDR_IN addr = { 0 };
	//初始化地址
	int port = mui->leport->text().toInt();
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);//端口号~65535尽量大于1W

	int r = ::connect(serverSocket, (SOCKADDR*)&addr, sizeof addr);

	struct sockaddr_in conn;
	memset(&conn, 0, sizeof(struct sockaddr_in));
	int len = sizeof(conn);
	int ret = ::getsockname(serverSocket, (SOCKADDR*)&conn, &len);

	mui->luser->setText(QString(QLatin1String(inet_ntoa(conn.sin_addr)))+QString::number(ntohs(conn.sin_port)));
	if (r == -1)//连接服务器失败!
	{
		return ;
	}
	CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)recvAndShow, NULL, NULL, NULL);
}

Ui设计

基于C++的Qt网络编程——聊天客户端_第2张图片

 服务端

myServer.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include 
#include 
#include "winsock2.h"
#include "stdlib.h"
#include "stdio.h"
#include "string"
#include 
#include 
#pragma comment (lib, "ws2_32.lib")

QT_BEGIN_NAMESPACE
namespace Ui { class MyServer; }
QT_END_NAMESPACE

class MyServer : public QWidget
{
    Q_OBJECT
public:
    MyServer(QWidget *parent = nullptr);
    ~MyServer();
private slots:
    void on_btopen_clicked();
    void on_btclose_clicked();
private:
    Ui::MyServer *ui;
};

class WorkThread : public QThread
{
public:
    WorkThread();
protected:
    void run();
};
void communication(LPVOID n);
#endif // MYSERVER_H

main.cpp

与客户端相同

myServer.cpp

#include "myserver.h"
#include "ui_myserver.h"
Ui::MyServer* mui;
SOCKET clientSocket[1024];
SOCKET serverSocket;
SOCKADDR_IN addr = { 0 };
int k = 0;

MyServer::MyServer(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyServer)
{
    ui->setupUi(this);
    mui = ui;
}
MyServer::~MyServer()
{
    delete ui;
}
void MyServer::on_btopen_clicked()
{
    WorkThread* thread = new WorkThread();
    thread->start();
}
void MyServer::on_btclose_clicked()
{
    closesocket(serverSocket);
    closesocket(*clientSocket);
    WSACleanup();
}

void communication(LPVOID n)
{
    char buff[256];
    int r;
    int i = (int)n;
    while (1)
    {
        struct sockaddr_in sa;
        int len = sizeof(sa);
        if (!getpeername(clientSocket[i - 1], (struct sockaddr*)&sa, &len))
        {
            //printf("对方IP:%s ", inet_ntoa(sa.sin_addr));
            //printf("对方PORT:%d ", ntohs(sa.sin_port));
        }
        memset(buff, 0, sizeof(buff));
        r = recv(clientSocket[i - 1], buff, 255, NULL);
        if (r > 0)
        {
            char* tem = buff;
            QString s = QString(QLatin1String(inet_ntoa(sa.sin_addr))) + ":" + QString::number(ntohs(sa.sin_port)) + ":" + QString::fromLocal8Bit(buff, 256);
            mui->tbinfor->append(s);
            for (int j = 0; j < k; j++)
            {
                QByteArray ba = s.toLocal8Bit();
                char* buff1 = ba.data();
                int len = send(clientSocket[j], buff1, strlen(buff1), NULL);
                if (len <= 0) {
                    return ;
                }
            }
        }

    }
}

WorkThread::WorkThread()
{
}

void WorkThread::run()
{
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData) != 0;//成功==0
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        return;
    }
    //创建socket
    //sockSer = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//AF=Address family ,ipv4,TCP,0
    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == INVALID_SOCKET)
    {
        return;
    }
    //初始化地址
    int port = mui->leport->text().toInt();
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);//端口号~65535
    int r = bind(serverSocket, (SOCKADDR*)&addr, sizeof(addr));
    if (r == -1)//绑定失败
    {
        return;
    }
    r = listen(serverSocket, 10);
    if (r == -1)//监听失败
    {
        return;
    }
    //连接
    //地址族
    SOCKADDR_IN cAddr = { 0 };
    int len = sizeof(cAddr);

    int i = 0;
    while (i < 1024)
    {
        clientSocket[i++] = accept(serverSocket, (sockaddr*)&cAddr, &len);
        k++;
        if (clientSocket[i - 1] == SOCKET_ERROR)//错误的客户端!
        {
            closesocket(serverSocket);
            WSACleanup();
            return;
        }
        mui->tbuser->append(QString(QLatin1String(inet_ntoa(cAddr.sin_addr))) + ":" + QString::number(ntohs(cAddr.sin_port)) + QString::fromLocal8Bit(" 上线了!"));
        CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)communication, (LPVOID)i, NULL, NULL);
    }
    return;
}

ui设计

基于C++的Qt网络编程——聊天客户端_第3张图片

实验结果

基于C++的Qt网络编程——聊天客户端_第4张图片

基于C++的Qt网络编程——聊天客户端_第5张图片

 

 

你可能感兴趣的:(网络,c++,qt)