编写一个基于Linux操作系统+C语言的聊天应用程序,使用QT实现两个主机端(服务器和客户端)进行图形化界面通信。

目录

一、任务及要求分析

二、实验过程

2.1 程序框架设计

2.1.1源文件导图

2.2.2 Server和Client窗口视图及属性介绍

2.2 数据库准备

2.3注册及登录

2.3.1 注册

2.3.2登录

2.3.3 通信

2.3.4 关闭服务器,断开连接

三、调试过程中出现的问题及相应解决办法

四、程序源码及其注释

4.1 Server.h

4.2 Server.cpp

4.3 main.cpp

4.4 client.h

4.5 client.cpp

4.6 main.cpp


任务

聊天工具的设计与实现

要求

编写一个基于Linux操作系统+ C语言的聊天应用程序,涉及的知识:多进程、进程通信、Qt、MySQL等知识。

基本功能

两个主机端(服务器和客户端)进行图形化界面通信;

                            一、任务及要求分析

编写一个基于Linux操作系统+ C语言的聊天应用程序,涉及的知识:多进程、进程通信、Qt、MySQL等知识。

  1. 创建两个主机端,分别为服务器Server和客户端Client,两个主机可以进行图形化界面通信。
  2. 使用mysql创建数据库ly,创建两个表user和chat,分别用于存储用户信息和通信内容信息。
  3. 使用QT实现图形化界面,通过QT Designer设计窗口。

 

                            二、实验过程

2.1 程序框架设计

2.1.1源文件导图

创建两个项目,分别人Client和Server,创建相关文件。

2.2.2 Server和Client窗口视图及属性介绍

使用QT自带的ui设计模块,按照如下图进行设计。

xx.ui文件在运行时,会自动创建ui_xx.h头文件。

 

 

2.2 数据库准备

在终端打开mysql,创建数据库ly,创建表user用于存储用户信息,创建表chat用于存储通信内容及相关信息。

为表user添加新用户:用户名为ly,密码为1234。

表chat为空。

 

2.3注册及登录

2.3.1 注册

输入用户名和密码,点击注册按钮,进行用户注册。注册成功显示“注册成功!”,否则显示“注册失败”。

注册成功后,在终端打开mysql,查询user表,新增一条记录。

2.3.2登录

输入用户名和密码,用户存在且密码正确显示“登录成功!”,否则显示“登录失败!”。

登录后,再次登录会显示“已登录”。

 

2.3.3 通信

在各自输入区输入信息后,点击发送,会同时显示到两个窗口的通信内容记录框内。

 

 同时,打开数据库查看chat表,新增记录。

2.3.4 关闭服务器,断开连接

 


               三、调试过程中出现的问题及相应解决办法

问题一:客户端无法连接到服务器。

解决办法:在Server.pro和Client.pro文件中添加语句:

                                     QT += network sql

 

问题二:通信内容不能是中文,如果输入中文会乱码。

解决方法:在数据传输过程中,采用utf-8编码。

 

问题三:使用QT连接mysql时,连接不上,显示错误信息“...not load”。Mysql配置有问题,QT版本过高(5.9)也会导致无法连接。

解决方法:重新配置mysql,下载低版本QT(5.6)。

 

问题四:安装完mysql后,第一次运行时,会提示输入密码,但密码是随机生成的,根本不知道密码,无法进入mysql。

解决方法:打开/etc/my.cnf文件,加上一行代码skip-grant-tables,表示无密码运行mysql。然后再直接用mysql -uroot登录,发现登录成功。然后执行sql语句修改密码 update user set Password='密码' where User='root'。最后把之前加的那段代码删掉,就可以使用自己的密码登录了。

 

问题五:编写完代码后成功运行,然后我想改一下文件名,改完之后,程序不能运行。报错:invalid use of incomplete type 'class Ui::Server'ui(new Ui:: Server)。

解决方法:在其对应的ui文件中,整个界面的ObjectName没有进行更改,打开其对应的ui文件,将其ObjectName更改即可。ObjectName即是在Designer界面下,选中控件后右边属性框的前列,修改名称后,重新构建,发现构建成功。


                                 四、程序源码及其注释

4.1 Server.h

#ifndef SERVER_H
#define SERVER_H

#include <QDialog>
#include <QTcpSocket>
#include <QTcpServer>
#include <QMessageBox>
#include <QDebug>
#include <QDateTime>
#include <QSqlError>
#include <QSqlDatabase>
#include <QSqlQuery>

namespace Ui {
class Server;
}

class Server : public QDialog
{
    Q_OBJECT

public:
    explicit Server(QWidget *parent = 0);
    ~Server();

private slots:
	// 定义函数,在cpp里实现。
    void on_stopButton_clicked();
    void acceptConnection();
    void sendMessage();
    void displayError(QAbstractSocket::SocketError);
    void receiveMessage();
    void saveMessage(QString , QString , QString);
private:
    Ui::Server *ui;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocketConnection;
};

#endif

4.2 Server.cpp

#include "server.h"
#include "ui_server.h"

Server::Server(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Server)
{
    ui->setupUi(this);
    tcpServer=new QTcpServer(this);
	// 调用listen监听端口,设置IP地址和端口号。
    if (!tcpServer->listen(QHostAddress::Any, 7777)) {
            qDebug() << tcpServer->errorString();
            close();
        }
    tcpSocketConnection = NULL;
	// 连接信号newConnection
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));
    // 将发送按钮和sendMessage函数关联起来
	connect(ui->pbtnSend,SIGNAL(clicked(bool)),this,SLOT(sendMessage()));
}

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

void Server::acceptConnection()
{
	// 调用nextPendingConnection获取连接进来的socket
    tcpSocketConnection = tcpServer->nextPendingConnection();
	// 根据不同的信号将tcpSocketConnection和相关函数关联
    connect(tcpSocketConnection,SIGNAL(disconnected()),this,SLOT(deleteLater()));
    connect(tcpSocketConnection,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
    // 接受Client发来的消息,readyRead()准备读取信号,异步读取数据。
	connect(tcpSocketConnection, SIGNAL(readyRead()), this, SLOT(receiveMessage()));
}

// 关闭服务器,断开连接。
void Server::on_stopButton_clicked()
{
    tcpSocketConnection->abort();
    QMessageBox::about(NULL,"Connection","服务器关闭,连接终止。");
}

// 向Client发送消息
void Server::sendMessage()
{
	//获取 输入框 里所输入的信息。
    QString str = ui->textEdit_input->text();
    //获取当前时间
	QDateTime time = QDateTime::currentDateTime();
    QString nowtime = time.toString("yyyy-MM-dd hh:mm:ss");
    //显示在Server的消息记录里
	ui->textEdit_log->append(nowtime + "    Server:");
    ui->textEdit_log->append("    " + str);

	tcpSocketConnection->write(ui->textEdit_input->text().toUtf8());
    // 将这条内容的有关信息存储到mysql。
	saveMessage(nowtime, "Server", str);
}

// 存储到mysql
void Server::saveMessage(QString time, QString user, QString content)
{
	// 连接并打开mysql
    QSqlDatabase dataBase=QSqlDatabase::addDatabase("QMYSQL");
    dataBase.setHostName("localhost");
    dataBase.setUserName("root");
    dataBase.setPassword("123456");
    dataBase.setDatabaseName("ly");
    dataBase.open();
    QSqlQuery query(dataBase);
    QString sql=QString("select *from chat");
    query.exec(sql);
    if(query.numRowsAffected() == 0)
    {
		// 将信息insert到chat表里。
          QString savesql = QString("INSERT INTO chat(time, user, content)");
          savesql += QString(" VALUES('%1','%2','%3')").arg(time).arg(user).arg(content);
     }
}
// 接收从Client发送来的消息。
void Server::receiveMessage()
{

    QDateTime time = QDateTime::currentDateTime();
    QString nowtime = time.toString("yyyy-MM-dd hh:mm:ss");
    // 使用readAll函数读取所有信息
	QString str = tcpSocketConnection->readAll();
    ui->textEdit_log->append(nowtime + "    Client:");
    ui->textEdit_log->append("    " + str);

}
// 异常信息
void Server::displayError(QAbstractSocket::SocketError)
{
     qDebug() << tcpSocketConnection->errorString();
}

4.3 main.cpp

#include "server.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Server w;
    w.setWindowTitle("Server");
    w.show();

    return a.exec();
}

4.4 client.h

#ifndef CLIENT_H
#define CLIENT_H

#include <QDialog>
#include <QTcpSocket>
#include <QObject>
#include <QMessageBox>
#include <QDebug>
#include <QDateTime>
#include <QSqlError>
#include <QSqlDatabase>
#include <QSqlQuery>

namespace Ui {
class Client;
}

class Client : public QDialog
{
    Q_OBJECT

public:
    explicit Client(QWidget *parent = 0);
    ~Client();

private slots:
    void on_connectButton_clicked();
    void receiveMessage();
    void displayError(QAbstractSocket::SocketError);
    void sendMessage();
    bool check(QString ,QString );
    void on_logonbutton_clicked();
    void saveMessage(QString , QString , QString);

private:
    Ui::Client *ui;
    QTcpSocket *tcpSocket;

};

#endif // CLIENT_H

4.5 client.cpp

#include "client.h"
#include "ui_client.h"

Client::Client(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Client)
{
    ui->setupUi(this);
    tcpSocket = new QTcpSocket(this);
	// 关联登录按钮和函数,进行确认登录并连接到服务器
    connect(ui->connectButton,SIGNAL(clicked()),this,SLOT(on_connectButton_clicked()));
    connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(displayError(QAbstractSocket::SocketError)));
    // 接受Server发来的消息,readyRead()准备读取信号,异步读取数据。
	connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(receiveMessage()));
	// 将发送按钮和sendMessage函数关联起来
    connect(ui->pbtnSend2,SIGNAL(clicked(bool)),this,SLOT(sendMessage()));

}

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

// 连接数据库,判断所输入的用户名和密码是否正确
bool Client::check(QString ID, QString PW)
{
    QSqlDatabase dataBase=QSqlDatabase::addDatabase("QMYSQL");
    dataBase.setHostName("localhost");
    dataBase.setUserName("vici");
    dataBase.setPassword("123456");
    dataBase.setDatabaseName("ly");
    dataBase.open();
    QSqlQuery showquery(dataBase);
    QString showsql=QString("select *from user;");
    showquery.exec(showsql);
    if(showquery.numRowsAffected() != 0)
    {
        while(showquery.next())
        {
            if(showquery.value(0).toString() == ID && showquery.value(1).toString() == PW)
                return true;
        }
    }
    return false;
}

// 登录按钮,登录信息正确则连接到服务器
void Client::on_connectButton_clicked()
{
    if(tcpSocket->state()!=QAbstractSocket::ConnectedState)
    {
		// 获取用户名和密码
        QString ID = ui->IDLineEdit->text();
        QString PW = ui->PWLineEdit->text();
		
		// 检查
        if(check(ID, PW))
        {
            tcpSocket->connectToHost("127.0.0.1", 7777);

            if(tcpSocket->waitForConnected(10000))
            {
                QMessageBox::about(NULL, "Connection", "登录成功!");
            }
            else
            {
                QMessageBox::about(NULL,"Connection","登录失败!");
            }
        }

    }
    else
        QMessageBox::information(NULL,"","已登录。");
}

// 发送信息,并存储到数据库
void Client::sendMessage()
{
    QString str = ui->textEdit_input2->text();
    QDateTime time = QDateTime::currentDateTime();
    QString nowtime = time.toString("yyyy-MM-dd hh:mm:ss");
    ui->textEdit_log2->append(nowtime + "    Client:");
    ui->textEdit_log2->append("    " + str);
    tcpSocket->write(ui->textEdit_input2->text().toUtf8()); //toLatin1
    saveMessage(nowtime, "Client", str);
}

// 接收Server发来的消息,并显示到消息记录框里。
void Client::receiveMessage()
{
    QDateTime time = QDateTime::currentDateTime();
    QString nowtime = time.toString("yyyy-MM-dd hh:mm:ss");
    QString str = tcpSocket->readAll();
    ui->textEdit_log2->append(nowtime + "    Server:");
    ui->textEdit_log2->append("    " + str);
}
// 将信息存储到数据库
void Client::saveMessage(QString time, QString user, QString content)
{
    QSqlDatabase dataBase=QSqlDatabase::addDatabase("QMYSQL");
    dataBase.setHostName("localhost");
    dataBase.setUserName("root");
    dataBase.setPassword("123456");
    dataBase.setDatabaseName("ly");
    dataBase.open();
    QSqlQuery query(dataBase);
    QString sql=QString("select *from chat");
    query.exec(sql);
    if(query.numRowsAffected() == 0)
    {
          QString savesql = QString("INSERT INTO chat(time, user, content)");
          savesql += QString(" VALUES('%1','%2','%3')").arg(time).arg(user).arg(content);
     }
}

// 注册按钮,并存储到数据库里。
void Client::on_logonbutton_clicked()
{
    QSqlDatabase dataBase=QSqlDatabase::addDatabase("QMYSQL");
    dataBase.setHostName("localhost");
    dataBase.setUserName("vici");
    dataBase.setPassword("123456");
    dataBase.setDatabaseName("ly");
    dataBase.open();
    QSqlQuery query(dataBase);
    QString ID = ui->IDLineEdit->text();
    QString PW = ui->PWLineEdit->text();
    QString sql=QString("select *from user");
    query.exec(sql);
    if(query.numRowsAffected() == 0)
    {
          QString savesql = QString("INSERT INTO user(ID, PW)");
          savesql += QString(" VALUES('%1','%2')").arg(ID).arg(PW);
          bool ok=query.exec(savesql);
          if(ok)
          {
               QMessageBox::about(NULL, "Save", "注册成功!");
          }
          else
          {
               QMessageBox::about(NULL, "Save", "注册失败!");
          }
     }
}

void Client::displayError(QAbstractSocket::SocketError)
{
    qDebug() << tcpSocket->errorString();
}

4.6 main.cpp

#include "client.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Client w;
    w.setWindowTitle("Client");
    w.show();

    return a.exec();
}

 

  • 6
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,以下是基于C语言编写的UDP通信程序,包含服务器客户端代码,服务器实现多线程编程的代码示例: 服务器代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <arpa/inet.h> #define MAX_MSG_SIZE 1024 #define SERVER_PORT 8888 #define MAX_CLIENTS 5 void *handle_client(void *arg) { int sockfd = *(int *)arg; char buf[MAX_MSG_SIZE]; struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); while (1) { memset(buf, 0, MAX_MSG_SIZE); int len = recvfrom(sockfd, buf, MAX_MSG_SIZE, 0, (struct sockaddr *)&client_addr, &addr_len); if (len == -1) { perror("recvfrom error"); continue; } printf("Received message from client %s:%d: %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buf); len = sendto(sockfd, buf, len, 0, (struct sockaddr *)&client_addr, addr_len); if (len == -1) { perror("sendto error"); } } close(sockfd); return NULL; } int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sockfd == -1) { perror("socket error"); exit(EXIT_FAILURE); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(SERVER_PORT); if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind error"); exit(EXIT_FAILURE); } printf("Server started.\n"); pthread_t threads[MAX_CLIENTS]; int thread_count = 0; while (thread_count < MAX_CLIENTS) { int *client_sockfd = malloc(sizeof(int)); *client_sockfd = sockfd; pthread_create(&threads[thread_count], NULL, handle_client, (void *)client_sockfd); thread_count++; } for (int i = 0; i < MAX_CLIENTS; i++) { pthread_join(threads[i], NULL); } close(sockfd); printf("Server stopped.\n"); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8888 #define MAX_MSG_SIZE 1024 int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sockfd == -1) { perror("socket error"); exit(EXIT_FAILURE); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(SERVER_PORT); while (1) { char buf[MAX_MSG_SIZE]; printf("Enter message to send: "); fgets(buf, MAX_MSG_SIZE, stdin); int len = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (len == -1) { perror("sendto error"); continue; } memset(buf, 0, MAX_MSG_SIZE); len = recvfrom(sockfd, buf, MAX_MSG_SIZE, 0, NULL, NULL); if (len == -1) { perror("recvfrom error"); continue; } printf("Received message from server: %s\n", buf); } close(sockfd); return 0; } ``` 在服务器,我们创建了一个UDP套接字,并将其绑定到本机的IP地址和口号上。然后,我们使用多线程来处理客户端发送的消息,每个线程处理一个客户端的消息。 在客户端,我们同样创建了一个UDP套接字,并将其连接到服务器的IP地址和口号上。然后,我们使用一个无限循环来读取用户输入的消息,并将其发送给服务器。同时,我们也接收服务器返回的消息,并将其打印到屏幕上。 希望这个示例能够帮助你了解如何基于C语言编写UDP通信程序,并在服务器使用多线程来处理客户端的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值