使用c/c++实现多线程TCP通信

本文讲到在qt环境下多线程实现的的c和c++的tcp通信,具体原理不做描述,如三次握手四次挥手。
服务器:
1、定义文件描述符,套接字结构体sockaddr_in。
2、创建socket(AF_INET,SOCK_STREAM,0)和给文件描述符赋值;
3、套接字结构体置0,bzero获取memset实现。
4、设置套接字结构体,分别有地址类型,端口和ip,注ip和端口的赋值涉及到字节序转换,需要用到htons,htonl,inet_addr等函数。
5、绑定文件描述符和套接字结构体。bind函数实现。
6、监听客户端,listen函数实现。
7、接收客户端请求,如果有请求创建新的套接字结构体和文件描述符,accept函数实现,注它为阻塞函数。
8、接收或者发送。recv和send函数实现,注recv默认为阻塞模式,关闭程序前需要使用close或者shutdown来关闭文件文件描述符。

客户端:
1、定义文件描述符和套接字结构体。
2、创建socket,且返回值赋给文件描述符。
3、清空套接字结构体。
4、设置套接字结构体。
5、连接服务器,使用connect函数实现。
6、接收或者发送。
使用c/c++实现多线程TCP通信_第1张图片
服务器

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "cthread.h"
#include "clientwidget.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void slotRead(QString str, sockaddr_in socket);
    void slotNewConnect(int socketfileId, sockaddr_in socketaddr);
private slots:
    void on_startlisten_pushButton_clicked();

    void on_craete_client_pushButton_clicked();

    void on_send_pushButton_clicked();
signals:
    void sendSig(QString str);
protected:
    void closeEvent(QCloseEvent *event);
private:
    Ui::Widget *ui;
    int m_serversocket;
    sockaddr_in m_socketaddr;
    QList<CThread*> m_threadList;
    QList<ClientWidget*> m_clientList;
};

#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include 
#include 

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    qRegisterMetaType<sockaddr_in>("sockaddr_in");

    //1、定义套接字,文件描述符,在头文件实现
    //2、创建socket  (第一个参数表示使用的地址类型,一般都是ipv4,AF_INET;第二个参数表示套接字类型:tcp:面向连接的稳定数据传输SOCK_STREAM;第三个参数设置为0)
    m_serversocket = socket(AF_INET,SOCK_STREAM,0);
    if (m_serversocket < 0)
    {
        QMessageBox::information(this,"error","创建套接字失败");
    }

    bzero(&m_socketaddr,sizeof(m_socketaddr));  //清空套接字

    //3、设置套接字  (初始化服务器端的套接字,并用htons和htonl将端口和地址由主机字节序转成网络字节序)
    m_socketaddr.sin_family = AF_INET;
    m_socketaddr.sin_port = htons(5555);
    m_socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    //4、绑定套接字  (绑定文件描述符和套接字结构体)
    int ret = bind(m_serversocket,(struct sockaddr*)&m_socketaddr,sizeof(m_socketaddr));
    if (ret < 0)
    {
        QMessageBox::information(this,"error","绑定主机失败");
    }
}

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

void Widget::closeEvent(QCloseEvent *event)
{
    for (int i = 0; i < m_clientList.size(); ++i)
        m_clientList.at(i)->close();
    for (int i = 0; i < m_threadList.size(); ++i) 
        m_threadList.at(i)->exitthread();
}

//开始监听
void Widget::on_startlisten_pushButton_clicked()
{
    //5、监听客户端,是否有新的连接
    int ret = listen(m_serversocket,5);
    if (ret < 0)
    {
        QMessageBox::information(this,"error","监听开启失败");
        return;
    }

    //创建线程,在线程中来循环接收客户端请求
    CThread* thread = new CThread(this,m_serversocket,1);
    connect(thread,SIGNAL(newconnect(int,sockaddr_in)),this,SLOT(slotNewConnect(int,sockaddr_in)));
    thread->start();

    m_threadList.append(thread);
}

//创建客户端
void Widget::on_craete_client_pushButton_clicked()
{
    ClientWidget* w = new ClientWidget;
    w->show();
    m_clientList.append(w);
}

//发送
void Widget::on_send_pushButton_clicked()
{
    emit sendSig(ui->send_textEdit->toPlainText());
}

//接收
void Widget::slotRead(QString str,sockaddr_in socket)
{
    ui->recv_textEdit->append(QString("端口:%1-%2").arg(socket.sin_port).arg(str));
}

//新来的客户端请求
void Widget::slotNewConnect(int socketfileId,sockaddr_in socketaddr)
{
    ui->listen_textEdit->append(QString("新连接:%1,地址:%2, 端口:%3").arg(socketfileId).arg(inet_ntoa(socketaddr.sin_addr)).arg(socketaddr.sin_port));

    //创建线程来接收消息和发送消息
    CThread* recvthread = new CThread(this,socketfileId,0,0);
    connect(recvthread,SIGNAL(recvSig(QString,sockaddr_in)),this,SLOT(slotRead(QString,sockaddr_in)));
    recvthread->setsocket(socketaddr);
    recvthread->start();

    CThread* sendthread = new CThread(this,socketfileId,0,1);
    connect(this,SIGNAL(sendSig(QString)),sendthread,SLOT(slotsend(QString)));
    sendthread->start();

    m_threadList.append(recvthread);
    m_threadList.append(sendthread);
}

客户端

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include 
#include 
#include 
#include 
#include 
#include "cthread.h"

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ClientWidget(QWidget *parent = nullptr);
    ~ClientWidget();

public slots:
    void slotRead(QString str, sockaddr_in socket);
private slots:
    void on_send_pushButton_clicked();
signals:
    void sendSig(QString str);
protected:
    void closeEvent(QCloseEvent *event);
private:
    Ui::ClientWidget *ui;
    int m_socketfileId;
    sockaddr_in m_clientsocket;
    QList<CThread*> m_threadList;
};

#endif // CLIENTWIDGET_H
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include 

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);
    qRegisterMetaType<sockaddr_in>("sockaddr_in");

    //1、定义套接字和文件描述符,在头文件实现
    //2、创建套接字
    m_socketfileId = socket(AF_INET,SOCK_STREAM,0);

    //3、设置套接字结构体
    m_clientsocket.sin_family = AF_INET;
    m_clientsocket.sin_port = htons(5555);
    m_clientsocket.sin_addr.s_addr = inet_addr("192.168.27.89");       //inet_addr从字符串类型转换为网络字符序

    //4、连接服务器
    if (::connect(m_socketfileId,(struct sockaddr*)&m_clientsocket,sizeof(m_clientsocket)) < 0)
    {
        QMessageBox::information(this,"error","连接失败");
        return ;
    }

    //创建线程来接收消息和发送消息
    CThread* recvthread = new CThread(this,m_socketfileId,0,0);
    connect(recvthread,SIGNAL(recvSig(QString,sockaddr_in)),this,SLOT(slotRead(QString,sockaddr_in)));
    recvthread->start();

    CThread* sendthread = new CThread(this,m_socketfileId,0,1);
    connect(this,SIGNAL(sendSig(QString)),sendthread,SLOT(slotsend(QString)));
    sendthread->start();

    m_threadList.append(recvthread);
    m_threadList.append(sendthread);
}

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

void ClientWidget::closeEvent(QCloseEvent *event)
{
    for (int i = 0; i < m_threadList.size(); ++i) {
        m_threadList.at(i)->exitthread();
    }
}

void ClientWidget::slotRead(QString str,sockaddr_in socket)
{
    ui->recv_textEdit->append(str);
}

void ClientWidget::on_send_pushButton_clicked()
{
    emit sendSig(ui->send_textEdit->toPlainText());
}

线程

#ifndef CTHREAD_H
#define CTHREAD_H

#include 
#include 
#include 
#include 
#include 

class CThread : public QThread
{
    Q_OBJECT
public:
    CThread(QObject* parent,int socketId,int type = 0,int model = 0);
    ~CThread();
    void run();
    void setsocket(sockaddr_in socket);
    void exitthread();
public slots:
    void slotsend(QString str);
signals:
    void recvSig(QString str,sockaddr_in);
    void newconnect(int socketfileId,sockaddr_in socket);
private:
    int m_socketclientfileId;
    int m_socketfileId;
    sockaddr_in m_clientsocket;
    sockaddr_in m_socket;
    QString m_sendstr;
    int m_type;
    int m_model;
    bool m_sendflag;
    bool m_exit;
};

#endif // CTHREAD_H

#include "cthread.h"
#include 
#include 

CThread::CThread(QObject *parent, int socketId, int type, int model)
    :QThread(parent),m_socketfileId(socketId),m_type(type),m_model(model),m_sendflag(false),m_exit(false)
{

}

CThread::~CThread()
{
    quit();
    wait();
}

void CThread::exitthread()
{
    shutdown(m_socketclientfileId, SHUT_WR);//关闭输出流
    shutdown(m_socketfileId, SHUT_WR);//关闭输出流
    m_exit = true;
}

void CThread::run()
{
    if (m_type == 1)
    {
        while (1)
        {
            if (m_exit)
                break;
            if (m_type == 1)
            {
                int len = sizeof(m_clientsocket);
                //6、接收客户端请求,accept为阻塞函数 (参数为文件描述符、新的连接套接字结构体和套接字长度。返回值:获取到的客户端文件描述符)
                m_socketclientfileId = accept(m_socketfileId,(struct sockaddr*)&m_clientsocket,(socklen_t*)&len);
                if (m_socketclientfileId < 0)
                {
                    QMessageBox::information(0,"error","接收开启失败");
                }
                else
                {
                    emit newconnect(m_socketclientfileId,m_clientsocket);
                }
            }
            QThread::msleep(200);
        }
    }
    else
    {
        while (1)
        {
            if (m_model == 0)
            {
                char buf[1024] = {0};
                //接收
                if (recv(m_socketfileId,buf,sizeof(buf),0) <= 0)
                    break;
                emit recvSig(QString::fromUtf8(buf),m_socket);
            }
            else if(m_model == 1)
            {
                if (m_sendflag)
                {
                    m_sendflag = false;
                    //发送
                    send(m_socketfileId,m_sendstr.toUtf8().data(),1024,0);
                }
            }
            if (m_exit)
                break;
            QThread::msleep(200);
        }
    }
}

void CThread::setsocket(sockaddr_in socket)
{
    m_socket = socket;
}

void CThread::slotsend(QString str)
{
    m_sendflag = true;
    m_sendstr = str;
}

你可能感兴趣的:(QT,c/c++,c语言,c++,tcp/ip)