QT简易群聊-基于腾讯云服务器

这段时间在学习QT,刚好学习到了QT中的socket网络编程,上网查找相关的资料,大部分都是在局域网中自己和自己玩。关于QT连接广域网、云服务器的资料少的可怜,也许是大家觉得太简单,不值得一写。谁让自己是个菜鸟,唉。接下来我来记录下整个过程,不为啥,只是为了记录下来,做个笔记,可能有些说法什么的不能入大佬的眼,不喜勿喷。
准备工作:
一台联网且带有Ubuntu虚拟机的Windows系统电脑;
腾讯云服务器
Windows版的QT Creator5.8软件
Xshell
Xftp
1.一台联网且带有Ubuntu虚拟机的Windows系统电脑
不知道如何安装虚拟机的小伙伴,请移步:
Ubuntu虚拟机安装
这里不做过多的赘述。
2.腾讯云服务器
刚刚接触这个的小伙伴,可能会有望而生畏的情绪在。觉得应该会很复杂,不可能一下就能使用,其实不然,至少在我们这个小工程里不存在复杂一说。
我之前用过阿里云,相对来说,阿里云的操作稍微要比腾讯云这边的操作要复杂些,也可能是我的错觉,因为这有个熟悉的过程。
腾讯云还是比较亲民的,对于学生来说,只需要10块钱便可让我们学习一个月,所以呢,这一块无需担心什么。
他的购买和注册登录步骤如下:
腾讯云服务器的购买、注册和登录
3.Windows版的QT Creator5.8软件
这个不是重点,请大家自己查阅资料安装。
4.Xshell和Xftp
这是两个软件,可自行到官网下载,前者是用来登录远程操作桌面的,后者是用来给Windows系统与云服务器主机之间传输文件的,他们的软件图标如下:
在这里插入图片描述
细心的小伙伴之前应该可以看到,在我们第2小点腾讯云服务器的购买、注册和登录一节中,它默认的实例,是有公网IP和内网IP的,如下:
在这里插入图片描述
这个公网IP,在后续的操作中,很重要。
好,在安装好两款软件后,我们双击打开Xshell软件,如图:
在这里插入图片描述
文件->新建,如下
在这里插入图片描述
主机一栏,填入公网IP号,其他按照上述填写即可,我们选择确定,输入登录密码,OK。如下:
在这里插入图片描述
现在,就已经OK了,在这里的一切操作,都是和在网页版的操作都是一样的。
接下来的Xftp操作,和Xshell大同小异,他连接上云服务器后的界面如下:
在这里插入图片描述
左边是我们电脑本机的文件,右边是我们服务器主机的文件,默认目录实在/home/ubuntu下,我们可以直接拖动文件完成左右文件的传输。在Xshell可以直接使用ls命令来看看是否传输成功,如下:
在这里插入图片描述
5.代码演示部分
我们将下面代码在Windows下编辑保存好,
myselect.c

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>

void InitServerAddr(struct sockaddr_in* addr)
{
    bzero(addr,sizeof(struct sockaddr_in));
    addr->sin_addr.s_addr = inet_addr("0.0.0.0");
    addr->sin_family = AF_INET;
    addr->sin_port = htons(9090);
}

#define prr_exit(x) do{perror(x);return -1;}while(0);

int main(void){
    int pic_fd = open("./mypic.jpg",O_RDONLY);
    if(pic_fd < 0)
    {
        perror("[open]");
        return -1;
    }


    int server_sockfd,client_sockfd;
    struct sockaddr_in server_addr;

    if ((server_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        prr_exit("[create server socket]");

    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");//服务器端的IP固定是这个。
    server_addr.sin_port = htons(9090);

    int opt = SO_REUSEADDR;
    setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    if (bind(server_sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
        prr_exit("[bind]");
    listen(server_sockfd,20);

    fd_set read_set,all_set;
    FD_ZERO(&all_set);
    FD_SET(server_sockfd,&all_set);
    int client_fd_array[FD_SETSIZE];
    for (int i = 0; i < FD_SETSIZE; i++)
        client_fd_array[i] = -1;
    int maxfd = server_sockfd,nready,nread; 
    char buf[128];
    char buf1[128];   
    int k = 0;
    char num[3] = "";
    int flag = -1;
    while(1)
    {
        read_set = all_set;
        for (int i = 0; i < FD_SETSIZE; i++)
        {
            if (maxfd < client_fd_array[i])
                maxfd = client_fd_array[i];
        }
        if ((nready = select(maxfd+1,&read_set,NULL,NULL,NULL)) < 0)
            prr_exit("[select]");

        if(FD_ISSET(server_sockfd,&read_set))
        {
            if ((client_sockfd = accept(server_sockfd,NULL,NULL)) < 0){
                prr_exit("[accept]");
            }
            FD_SET(client_sockfd,&all_set);
            for (int i = 0; i < FD_SETSIZE; i++)
            {
                if (client_fd_array[i] < 0)
                {
                    client_fd_array[i] = client_sockfd;
                    if (maxfd < client_sockfd)
                        maxfd = client_sockfd;
                    break;
                }
            }
            if (--nready == 0)
                continue;
        }

        for (int i = 0; i < maxfd+1; i++)
        {
            if (client_fd_array[i] < 0)
                continue;
            if (FD_ISSET(client_fd_array[i],&read_set))
            {
                bzero(buf,128);
                bzero(buf1,128);
                if ((nread = read(client_fd_array[i],buf,128)) == 0)
                {
                    FD_CLR(client_fd_array[i],&all_set);
                    client_fd_array[i] = -1;
                    close(client_fd_array[i]);
                }
                else if (nread > 0)
                {
                    if(buf[0] != '^' && buf[1] != '|' && buf[2] != '3')
                    {
                        for(int j = 0; j < FD_SETSIZE; j++)
                        {
                            sprintf(buf1,"[%d]%s",client_fd_array[i],buf);
                            write(client_fd_array[j],buf1,strlen(buf1));
                        }
                        printf ("recv form client = %s\n",buf);

                        if(buf[0] == '$')
                        {
                            bzero(num,sizeof(num));
                            num[0] = buf[1];
                            num[1] = buf[2];
                            for(k = 0;k < FD_SETSIZE; k++)
                            {
                                if(client_fd_array[k] == atoi(num))
                                {
                                    while(1)
                                    { 
                                        bzero(buf,sizeof(buf));
                                        int pic_len = read(pic_fd,buf,sizeof(buf));

                                        write(client_fd_array[k],buf,pic_len);
                                        if(pic_len < 128)  {flag = 1;break;}
                                    }
                                }
                                if(flag == 1) 
                                {
                                    bzero(num,sizeof(num));
                                    lseek(pic_fd,0,SEEK_SET); 
                                    flag = -1;
                                    break;
                                }
                                if(k == FD_SETSIZE - 1)
                                {
                                    bzero(num,sizeof(num));
                                    break;
                                }


                            }
                        }
                    }
                    bzero(buf,sizeof(buf));
                    if (--nready == 0)
                        break;
                }
            }

        }

        //for(int i = 0;i )
    }

    return 0;
}

然后通过Xftp,直接从界面的左边拖到界面的右边,在Xshell登录的Ubuntu终端界面,直接gcc myselect.c -o myselect编译即可,然后./myselect执行。

好,下面是QT端的代码:
main.cpp

#include "tcpclientgui.h"
#include <QApplication>
#include <QTextCodec>

int main(int argc, char *argv[])
{
     QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
     QApplication a(argc, argv);
     TcpClientGui w;
     w.show();

     return a.exec();
}

tcpclientgui.h

#ifndef TCPCLIENTGUI_H
#define TCPCLIENTGUI_H

#include <QWidget>
#include <QListWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTcpSocket>
#include <QTime>
#include <QTimer>
#include <QHostAddress>

class TcpClientGui : public QWidget
{
    Q_OBJECT

public:
    TcpClientGui(QWidget *parent = 0);
    ~TcpClientGui();

    QListWidget *listwidget;
    QLineEdit *ipedit;
    QTextEdit *textedit;
    QTcpSocket *tcpsocket;
    bool isconnect; //保存是否连接

public slots:
    void slotConnect();
    void slotSendData();
    void slotSlipSendData();
    void slotRecvData();
};

#endif // TCPCLIENTGUI_H

tcpclientgui.cpp

#include "tcpclientgui.h"

TcpClientGui::TcpClientGui(QWidget *parent)
    : QWidget(parent)
{
    this->setWindowTitle("TCP客户端");
    isconnect = false;
    listwidget = new QListWidget;
    ipedit = new QLineEdit;
    QPushButton *connect_button = new QPushButton("连接");
    this->connect(connect_button,SIGNAL(clicked(bool)),this,SLOT(slotConnect()));
    QHBoxLayout *connect_layout  = new QHBoxLayout;
    connect_layout->addWidget(ipedit);
    connect_layout->addWidget(connect_button);

    textedit = new QTextEdit;
    textedit->setMaximumHeight(50);
    QPushButton *send_button = new QPushButton("发 送");

    this->connect(send_button,SIGNAL(clicked(bool)),this,SLOT(slotSendData()));
    QPushButton *cancel_button = new QPushButton("取 消");
    this->connect(cancel_button,SIGNAL(clicked(bool)),this,SLOT(close()));
    QHBoxLayout *send_layout = new QHBoxLayout;
    send_layout->addStretch();
    send_layout->addWidget(send_button);
    send_layout->addWidget(cancel_button);

    QVBoxLayout *vlayout = new QVBoxLayout(this);
    vlayout->addWidget(listwidget);
    vlayout->addLayout(connect_layout);
    vlayout->addWidget(textedit);
    vlayout->addLayout(send_layout);

    QTimer* timer = new QTimer;
    this->connect(timer,SIGNAL(timeout()),this,SLOT(slotSlipSendData()));/*第一个参数是信号发出者,第二个参数是发出的信号,第三个信号是信号的接收者,第四个参数是调用的函数*/
    /*启动定时器,如果不想这个时候启动 ,那下面这句到需要的时候再写*/
    timer->start(5000);//时间单位是ms

    tcpsocket = new QTcpSocket(this);
}

void TcpClientGui::slotConnect()
{
    QString ipstr = ipedit->text();     //获取连接的ip
    unsigned short port = 9090;
    if(!ipstr.trimmed().isEmpty())
    {
        tcpsocket->connectToHost(QHostAddress(ipstr),port); //连接到服务器
        qDebug() << "connect ok" << endl;
        isconnect = true;
        this->connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(slotRecvData()));     //连接有数据可读信号到接收数据的槽
        listwidget->addItem("connected to " + ipstr + " " + QTime::currentTime().toString("hh:mm"));
        listwidget->setCurrentRow(listwidget->count() - 1);
    }
}

void TcpClientGui::slotSendData()
{
    QString mess = "";
   QString text = "";
    if(isconnect)
    {
        mess = textedit->toPlainText();
        tcpsocket->write(mess.toLatin1());
        text = "send to : " + ipedit->text() + " : " + QTime::currentTime().toString("hh:mm") + "\n" + mess;
        listwidget->addItem(mess);
        listwidget->setCurrentRow(listwidget->count() - 1);
        textedit->clear();
    }
}

void TcpClientGui::slotSlipSendData()
{
    QString mess = "^|3";
    QString text = "";
    if(isconnect)
    {
        //mess = textedit->toPlainText();
       tcpsocket->write(mess.toLatin1());
        text = "send to : " + ipedit->text() + " : " + QTime::currentTime().toString("hh:mm") + "\n" + mess;
       // listwidget->addItem(text);
       // listwidget->setCurrentRow(listwidget->count() - 1);
        //textedit->clear();
    }
}

void TcpClientGui::slotRecvData()
{
    QString text = "";
    QByteArray array = tcpsocket->readAll();
    text = "rect from : " + ipedit->text() + " : " + QTime::currentTime().toString("hh:mm") + "\n" + array;
    listwidget->addItem(text);
    listwidget->setCurrentRow(listwidget->count() - 1);
}

TcpClientGui::~TcpClientGui()
{
    tcpsocket->disconnectFromHost();
    tcpsocket->deleteLater();
    tcpsocket = NULL;
}

QT界面如下:
在这里插入图片描述
完成这些后,我们编译运行QT端代码结果如下:
在这里插入图片描述
在连接一栏,输入我们云服务器主机的公网IP,点击连接,如下:
在这里插入图片描述
TCP客户端输入Hello,云服务器端接收到,我们可以多开两个客户端看看效果。至此,我们完成了我们需要的简易效果-------QT群聊。

几点说明:QT编译运行后生成的.exe文件,放在别的主机上是运行不了的(没有安装配置相关环境)。那么我们可以通过这样的方式来解决:
QT编译后生成的.exe文件在别的主机上运行的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值