基于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())。
詳細設計?
程序流程圖
?實現代碼
客戶端
myClient.h
#ifndef MYCLIENT_H #define MYCLIENT_H#include <QWidget> #include <iostream> #include "winsock2.h" #include "stdlib.h" #include "stdio.h" #include "string" #include <QThread> #pragma comment (lib, "ws2_32.lib")QT_BEGIN_NAMESPACE namespace Ui { class MyClient; } QT_END_NAMESPACEclass 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_Hmain.cpp
主要是解決中文亂碼問題,這里給出解決方案的一種
//添加庫 #include <QTextCodec> //設置編碼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;//成功==0if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)//請求版本失敗{return ;}//創建socketserverSocket = 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盡量大于1Wint 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設計
?服務端
myServer.h
#ifndef MYSERVER_H #define MYSERVER_H#include <QWidget> #include <iostream> #include "winsock2.h" #include "stdlib.h" #include "stdio.h" #include "string" #include <QByteArray> #include <QThread> #pragma comment (lib, "ws2_32.lib")QT_BEGIN_NAMESPACE namespace Ui { class MyServer; } QT_END_NAMESPACEclass 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_Hmain.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;//成功==0if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){return;}//創建socket//sockSer = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//AF=Address family ,ipv4,TCP,0serverSocket = 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);//端口號~65535int 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网络编程——聊天客户端的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vim编辑器的使用--转自MJ学长
- 下一篇: 前端学习(3346):设计模式之工厂模式