精华内容
下载资源
问答
  • QT 之TCP网络编程(非常值得看的一篇博客!)
    万次阅读 多人点赞
    2018-05-19 19:26:06
    首先介绍一下TCP:(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。相比而言UDP,就是开放式、无连接、不可靠的传输层通信协议。
    下面,我一次进行客户端和服务器端的QT实现。我的开发环境是:QT Creator 5.7。

    先看下效果图: 
    one server---two clients

    一:客户端编程

    QT提供了QTcpSocket类,可以直接实例化一个客户端,可在help中索引如下:

    The QTcpSocket class provides a TCP socket. More...
    Header      #include <QTcpSocket> 
    qmake       QT += network
    Inherits:   QAbstractSocket
    Inherited By:   QSslSocket
    从这里,我们可以看到,必须要在.pro文件中添加QT += network才可以进行网络编程,否则是访问不到<QTcpSocket>头文件的。
    
    客户端读写相对简单,我们看一下代码头文件:
    #ifndef MYTCPCLIENT_H
    #define MYTCPCLIENT_H
    
    #include <QMainWindow>
    #include <QTcpSocket>
    #include <QHostAddress>
    #include <QMessageBox>
    namespace Ui {
    class MyTcpClient;
    }
    
    class MyTcpClient : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MyTcpClient(QWidget *parent = 0);
        ~MyTcpClient();
    
    private:
        Ui::MyTcpClient *ui;
        QTcpSocket *tcpClient;
    
    private slots:
        //客户端槽函数
        void ReadData();
        void ReadError(QAbstractSocket::SocketError);
    
        void on_btnConnect_clicked();
        void on_btnSend_clicked();
        void on_pushButton_clicked();
    };
    
    #endif // MYTCPCLIENT_H
    •  
    我们在窗口类中,定义了一个私有成员QTcpSoket *tcpClient。

    1) 初始化QTcpSocket 
    在构造函数中,我们需要先对其进行实例化,并连接信号与槽函数:

        //初始化TCP客户端
        tcpClient = new QTcpSocket(this);   //实例化tcpClient
        tcpClient->abort();                 //取消原有连接
        connect(tcpClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
        connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), \
                this, SLOT(ReadError(QAbstractSocket::SocketError)));
    •  

    2)建立连接 和 断开连接

        tcpClient->connectToHost(ui->edtIP->text(), ui->edtPort->text().toInt());
        if (tcpClient->waitForConnected(1000))  // 连接成功则进入if{}
        {
            ui->btnConnect->setText("断开");
            ui->btnSend->setEnabled(true);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    a)建立TCP连接的函数:void connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite)是从QAbstractSocket继承下来的public function,同时它又是一个virtual function。作用为:Attempts to make a connection to address on port port。
    b)等待TCP连接成功的函数:bool waitForConnected(int msecs = 30000)同样是从QAbstractSocket继承下来的public function,同时它又是一个virtual function。作用为:Waits until the socket is connected, up to msecs milliseconds. If the connection has been established, this function returns true; otherwise it returns false. In the case where it returns false, you can call error() to determine the cause of the error.
    
    上述代码中,edtIP, edtPort是ui上的两个lineEditor,用来填写服务器IP和端口号。btnConnect是“连接/断开”复用按钮,btnSend是向服务器发送数据的按钮,只有连接建立之后,才将其setEnabled。
            tcpClient->disconnectFromHost();
            if (tcpClient->state() == QAbstractSocket::UnconnectedState \
                    || tcpClient->waitForDisconnected(1000))  //已断开连接则进入if{}
            {
                ui->btnConnect->setText("连接");
                ui->btnSend->setEnabled(false);
            }
    a)断开TCP连接的函数:void disconnectFromHost()是从QAbstractSocket继承的public function,同时它又是一个virtual function。作用为:Attempts to close the socket. If there is pending data waiting to be written, QAbstractSocket will enter ClosingState and wait until all data has been written. Eventually, it will enter UnconnectedState and emit the disconnected() signal.
    b)等待TCP断开连接函数:bool waitForDisconnected(int msecs = 30000),同样是从QAbstractSocket继承下来的public function,同时它又是一个virtual function。作用为:Waits until the socket has disconnected, up to msecs milliseconds. If the connection has been disconnected, this function returns true; otherwise it returns false. In the case where it returns false, you can call error() to determine the cause of the error.

    3)读取服务器发送过来的数据 
    readyRead()是QTcpSocket从父类QIODevice中继承下来的信号:This signal is emitted once every time new data is available for reading from the device’s current read channel。 
    readyRead()对应的槽函数为:

    void MyTcpClient::ReadData()
    {
        QByteArray buffer = tcpClient->readAll();
        if(!buffer.isEmpty())
        {
            ui->edtRecv->append(buffer);
        }
    }
    •  
    readAll()是QTcpSocket从QIODevice继承的public function,直接调用就可以读取从服务器发过来的数据了。我这里面把数据显示在textEditor控件上(ui>edtRecv)。由此完成了读操作。
    error(QAbstractSocket::SocketError)是QTcpSocket从QAbstractSocket继承的signal, This signal is emitted after an error occurred. The socketError parameter describes the type of error that occurred.连接到的槽函数定义为:
    void MyTcpClient::ReadError(QAbstractSocket::SocketError)
    {
        tcpClient->disconnectFromHost();
        ui->btnConnect->setText(tr("连接"));
        QMessageBox msgBox;
        msgBox.setText(tr("failed to connect server because %1").arg(tcpClient->errorString()));
    xec();
    }
    •  
    这段函数的作用是:当错误发生时,首先断开TCP连接,再用QMessageBox提示出errorString,即错误原因。

    4)向服务器发送数据

        QString data = ui->edtSend->toPlainText();
        if(data != "")
        {
            tcpClient->write(data.toLatin1()); //qt5去除了.toAscii()
        }
    定义一个QString变量,从textEditor(edtSend)中获取带发送数据,write()是QTcpSocket从QIODevice继承的public function,直接调用就可以向服务器发送数据了。这里需要注意的是:toAscii()到qt5就没有了,这里要写成toLatin1()。

    至此,通过4步,我们就完成了TCP Client的程序开发,源码下载地址:客户端qt程序源码

    二:服务器端编程 
    服务器段编程相比于客户端要繁琐一些,因为对于客户端来说,只能连接一个服务器。而对于服务器来说,它是面向多连接的,如何协调处理多客户端连接就显得尤为重要。 
    前言:编程过程中遇到的问题 和 解决的方法 
    遇到的问题:每个新加入的客户端,服务器给其分配一个SocketDescriptor后,就会emit newConnection()信号,但分配好的SocketDecriptor并没有通过newConnection()信号传递,所以用户得不到这个客户端标识SocketDescriptor。同样的,每当服务器收到新的消息时,客户端会emit readReady()信号,然而readReady()信号也没有传递SocketDescriptor, 这样的话,服务器端即使接收到消息,也不知道这个消息是从哪个客户端发出的。

    解决的方法: 
    1. 通过重写[virtual protected] void QTcpServer::incomingConnection(qintptr socketDescriptor),获取soketDescriptor。自定义TcpClient类继承QTcpSocket,并将获得的soketDescriptor作为类成员。 这个方法的优点是:可以获取到soketDescriptor,灵活性高。缺点是:需要重写函数、自定义类。 
    2. 在newConnection()信号对应的槽函数中,通过QTcpSocket *QTcpServer::nextPendingConnection()函数获取 新连接的客户端:Returns the next pending connection as a connected QTcpSocket object. 虽然仍然得不到soketDescriptor,但可以通过QTcpSocket类的peerAddress()和peerPort()成员函数获取客户端的IP和端口号,同样是唯一标识。 优点:无需重写函数和自定义类,代码简洁。缺点:无法获得SocketDecriptor,灵活性差。

    本文介绍第二种方法:

    QT提供了QTcpServer类,可以直接实例化一个客户端,可在help中索引如下:

    The QTcpServer class provides a TCP-based server. More...
    Header:     #include <QTcpServer> 
    qmake:      QT += network
    Inherits:       QObject
    •  
    从这里,我们可以看到,必须要在.pro文件中添加QT += network才可以进行网络编程,否则是访问不到<QTcpServer>头文件的。
    
    我们看一下代码头文件:
    #ifndef MYTCPSERVER_H
    #define MYTCPSERVER_H
    
    #include <QMainWindow>
    #include <QTcpServer>
    #include <QTcpSocket>
    #include <QNetworkInterface>
    #include <QMessageBox>
    namespace Ui {
    class MyTcpServer;
    }
    
    class MyTcpServer : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MyTcpServer(QWidget *parent = 0);
        ~MyTcpServer();
    
    private:
        Ui::MyTcpServer *ui;
        QTcpServer *tcpServer;
        QList<QTcpSocket*> tcpClient;
        QTcpSocket *currentClient;
    
    private slots:
        void NewConnectionSlot();
        void disconnectedSlot();
        void ReadData();
    
        void on_btnConnect_clicked();
        void on_btnSend_clicked();
        void on_btnClear_clicked();
    };
    
    #endif // MYTCPSERVER_H
    •  
    值得注意的是,在服务端编写时,需要同时定义服务器端变量QTcpServer *tcpServer和客户端变量 QList<QTcpSocket*> tcpClient。tcpSocket QList存储了连接到服务器的所有客户端。因为QTcpServer并不是QIODevice的子类,所以在QTcpServer中并没有任何有关读写操作的成员函数,读写数据的操作全权交由QTcpSocket处理。
    
    •  

    1)初始化QTcpServer

        tcpServer = new QTcpServer(this);
        ui->edtIP->setText(QNetworkInterface().allAddresses().at(1).toString());   //获取本地IP
        ui->btnConnect->setEnabled(true);
        ui->btnSend->setEnabled(false);
    
        connect(tcpServer, SIGNAL(newConnection()), this, SLOT(NewConnectionSlot()));
    通过QNetworkInterface().allAddresses().at(1)获取到本机IP显示在lineEditor上(edtIP)。介绍如下:
    [static] QList<QHostAddress> QNetworkInterface::allAddresses()
    This convenience function returns all IP addresses found on the host machine. It is equivalent to calling addressEntries() on all the objects returned by allInterfaces() to obtain lists of QHostAddress objects then calling QHostAddress::ip() on each of these.:
    
    每当新的客户端连接到服务器时,newConncetion()信号触发,NewConnectionSlot()是用户的槽函数,定义如下:
    void MyTcpServer::NewConnectionSlot()
    {
        currentClient = tcpServer->nextPendingConnection();
        tcpClient.append(currentClient);
        ui->cbxConnection->addItem(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1])\
                                              .arg(currentClient->peerPort()));
        connect(currentClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
        connect(currentClient, SIGNAL(disconnected()), this, SLOT(disconnectedSlot()));
    }
    通过nextPendingConnection()获得连接过来的客户端信息,取到peerAddress和peerPort后显示在comboBox(cbxConnection)上,并将客户端的readyRead()信号连接到服务器端自定义的读数据槽函数ReadData()上。将客户端的disconnected()信号连接到服务器端自定义的槽函数disconnectedSlot()上。

    2)监听端口 与 取消监听

         bool ok = tcpServer->listen(QHostAddress::Any, ui->edtPort->text().toInt());
         if(ok)
         {
             ui->btnConnect->setText("断开");
             ui->btnSend->setEnabled(true);
         }
    a)监听端口的函数:bool QTcpServer::listen(const QHostAddress &*address* = QHostAddress::Any, quint16 *port* = 0),该函数的作用为:Tells the server to listen for incoming connections on address *address* and port *port*. If port is 0, a port is chosen automatically. If address is QHostAddress::Any, the server will listen on all network interfaces.
    Returns true on success; otherwise returns false.
         for(int i=0; i<tcpClient.length(); i++)//断开所有连接
         {
             tcpClient[i]->disconnectFromHost();
             bool ok = tcpClient[i]->waitForDisconnected(1000);
             if(!ok)
             {
                 // 处理异常
             }
             tcpClient.removeAt(i);  //从保存的客户端列表中取去除
         }
         tcpServer->close();     //不再监听端口
    b)断开客户端与服务器连接的函数:disconnectFromHost()和waitForDisconnected()上文已述。断开连接之后,要将其从QList tcpClient中移除。服务器取消监听的函数:tcpServer->close()。
        //由于disconnected信号并未提供SocketDescriptor,所以需要遍历寻找
        for(int i=0; i<tcpClient.length(); i++)
        {
            if(tcpClient[i]->state() == QAbstractSocket::UnconnectedState)
            {
                // 删除存储在combox中的客户端信息
                ui->cbxConnection->removeItem(ui->cbxConnection->findText(tr("%1:%2")\
                                      .arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1])\
                                      .arg(tcpClient[i]->peerPort())));
                // 删除存储在tcpClient列表中的客户端信息
                 tcpClient[i]->destroyed();
                 tcpClient.removeAt(i);
            }
        }
    c)若某个客户端断开了其与服务器的连接,disconnected()信号被触发,但并未传递参数。所以用户需要遍历tcpClient list来查询每个tcpClient的state(),若是未连接状态(UnconnectedState),则删除combox中的该客户端,删除tcpClient列表中的该客户端,并destroy()。
    
    •  

    3)读取客户端发送过来的数据

        // 客户端数据可读信号,对应的读数据槽函数
        void MyTcpServer::ReadData()
        {
            // 由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端
            for(int i=0; i<tcpClient.length(); i++)
            {
                QByteArray buffer = tcpClient[i]->readAll();
                if(buffer.isEmpty())    continue;
    
                static QString IP_Port, IP_Port_Pre;
                IP_Port = tr("[%1:%2]:").arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1])\
                                             .arg(tcpClient[i]->peerPort());
    
                // 若此次消息的地址与上次不同,则需显示此次消息的客户端地址
                if(IP_Port != IP_Port_Pre)
                    ui->edtRecv->append(IP_Port);
    
                ui->edtRecv->append(buffer);
    
                //更新ip_port
                IP_Port_Pre = IP_Port;
            }
        }
    这里需要注意的是,虽然tcpClient产生了readReady()信号,但readReady()信号并没有传递任何参数,当面向多连接客户端时,tcpServer并不知道是哪一个tcpClient是数据源,所以这里遍历tcpClient列表来读取数据(略耗时,上述的解决方法1则不必如此)。
    读操作由tcpClient变量处理:tcpClient[i]->readAll();

    4)向客户端发送数据

        //全部连接
        if(ui->cbxConnection->currentIndex() == 0)
        {
            for(int i=0; i<tcpClient.length(); i++)
                tcpClient[i]->write(data.toLatin1()); //qt5除去了.toAscii()
        }
    a)向当前连接的所有客户端发数据,遍历即可。
        //指定连接
        QString clientIP = ui->cbxConnection->currentText().split(":")[0];
        int clientPort = ui->cbxConnection->currentText().split(":")[1].toInt();
        for(int i=0; i<tcpClient.length(); i++)
        {
            if(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]==clientIP\
                            && tcpClient[i]->peerPort()==clientPort)
            {
                tcpClient[i]->write(data.toLatin1());
                return; //ip:port唯一,无需继续检索
            }
        }
    b)在comboBox(cbxConnction)中选择指定连接发送数据:通过peerAddress和peerPort匹配客户端,并发送。写操作由tcpClient变量处理:tcpClient[i]->write()。

    至此,通过4步,我们就完成了TCP Server的程序开发,源码下载地址:服务器端qt程序源码

    转载博客地址为: https://blog.csdn.net/u011964923/article/details/64923685

     

     

    微信返利机器人,公众号骗局揭秘

    更多相关内容
  • C++ TCP网络编程,包括服务器和客户端,运行的时候找到debug下的TCP_Server.exe先双击运行,后双击运行TCP_Clinet.exe
  • 基于Linux的TCP网络编程。。。。。。。。。。。。。。。。。。。。。。。。
  • TCP 网络编程

    2016-06-07 07:28:11
    TCP网络编程源码
  • C++基于Socket的TCP网络编程IPv6,先运行debug目录下的TCP_Server.exe,再运行TCP_Clinet.exe
  • qt之tcp实现网络通信和聊天室,代码附带注释和讲解,适合初学者
  • TCP网络编程

    2014-08-05 08:43:08
    TCP/UDP网络应用开发,网络基础开发教程
  • 戏说TCP网络编程.doc

    2021-09-29 20:29:18
    戏说TCP网络编程.doc
  • TCP IP网络编程

    热门讨论 2015-06-02 22:23:10
    TCP/IP网络编程涵盖操作系统、系统编程、TCP/IP 协议等多种内容,结构清晰、讲解细致、通俗易懂。书中收录丰富示例,详细展现了Linux 和Windows 平台下套接字编程的共性与个性。特别是从代码角度说明了不同模型...
  • 基于TCP网络编程实现代码:客户端与服务器数据通信
  • Qt Tcp网络编程

    千次阅读 2022-04-08 14:30:50
    OSI模型共有七层,从下到上分别是物理层、数据链路层、网络层、运输层、会话层、表示层和应用层。但是这显然是有些复杂的,所以在TCP/IP协议中,它们被简化为了四个层次。来自百度 TCP面向连接 是指发送数据之前必须...

    概念

    TCP/IP协议在一定程度上参考了OSI的体系结构。OSI模型共有七层,从下到上分别是物理层、数据链路层、网络层、运输层、会话层、表示层和应用层。但是这显然是有些复杂的,所以在TCP/IP协议中,它们被简化为了四个层次。来自百度

    Tcp通信的特点

    1 面向连接:是指发送数据之前必须在两端建立连接,“三次握手”
    2.提供可靠的服务:Tcp通过校验和,使用重传控制、流量控制和拥塞控制
    3.每条传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式
    4…面向字节流
    套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
    套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

    效果

    在这里插入图片描述

    QT提供了QTcpSocket类,可以方便的实现一个客户端;
    .h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include <QTcpSocket>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private slots:
        void on_btnClearRec_clicked();
    
        //接受数据
        void receiveData();
    
        //处理错误信息
        void ReadError(QAbstractSocket::SocketError);
    
        void on_btn_Connect_clicked();
    
        void on_btn_DisConnect_clicked();
    
        void on_btnSend_clicked();
    
    private:
        Ui::Widget *ui;
        //套接字    
        QTcpSocket *m_Socket = nullptr;
    
    
    };
    #endif // WIDGET_H
    
    

    .cpp

    #include "widget.h"
    #include "ui_widget.h"
    #include<QNetworkProxy>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        ui->btn_DisConnect->setEnabled(false);
    
        m_Socket = new QTcpSocket(this);
    
        //设置代理模式
        m_Socket->setProxy(QNetworkProxy::NoProxy);
    
        //读取新数据信号槽
        connect(m_Socket, SIGNAL(readyRead()), this, SLOT(receiveData()));
    
        //连接错误信息信号槽
        connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)),
                this, SLOT(ReadError(QAbstractSocket::SocketError)));
    
        //调用connectToHost()并成功建立连接之后发出此信号。
        connect(m_Socket, &QTcpSocket::connected,[&]{
            ui->textEdit_Receive->append(QStringLiteral("连接成功"));
        });
    
        //当套接字断开连接时发出此信号
        connect(m_Socket, &QTcpSocket::disconnected,[&]{
            ui->textEdit_Receive->append(QStringLiteral("断开连接"));
        });
    
    
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    
    void Widget::on_btnClearRec_clicked()
    {
        ui->textEdit_Receive->clear();
    }
    
    void Widget::receiveData()
    {
        QByteArray buffer = m_Socket->readAll();
        if(!buffer.isEmpty())
        {
           ui->textEdit_Receive->append(buffer);
        }
    }
    
    void Widget::ReadError(QAbstractSocket::SocketError)
    {
        m_Socket->disconnectFromHost();
        ui->textEdit_Receive->append(m_Socket->errorString());
    }
    
    void Widget::on_btn_Connect_clicked()
    {
        //连接服务器
        m_Socket->connectToHost(ui->lineEditI_Ip->text(), ui->lineEdit_Port->text().toInt());
        if (m_Socket->waitForConnected(1000))
        {
            ui->btn_Connect->setEnabled(false);
            ui->btn_DisConnect->setEnabled(true);
    
        }
    }
    
    void Widget::on_btn_DisConnect_clicked()
    {
        //断开连接
        m_Socket->disconnectFromHost();
        if (m_Socket->state() == QAbstractSocket::UnconnectedState
             || m_Socket->waitForDisconnected(1000))
        {
         ui->btn_Connect->setEnabled(true);
         ui->btn_DisConnect->setEnabled(false);
        }
    }
    
    void Widget::on_btnSend_clicked()
    {
        QString data = ui->textEdit_Send->toPlainText();
        if(data != "")
        {
            //发送数据
            m_Socket->write(data.toLatin1());
        }
    }
    
    

    QT提供了QTcpServer类,可以方便的实现一个服务端;
    .h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include <QTcpServer>
    #include <QTcpSocket>
    #include <QNetworkInterface>
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private slots:
        //接受信息
        void receiveData();
    
        void on_btn_Connect_clicked();
    
        void on_btn_DisConnect_clicked();
    
        void on_btnSend_clicked();
    
    private:
        Ui::Widget *ui;
        //服务器
        QTcpServer *m_tcpServer = nullptr;
        
        //套接字
        QTcpSocket *m_tcpSocket = nullptr;
    
    };
    #endif // WIDGET_H
    

    .cpp文件

    #include "widget.h"
    #include "ui_widget.h"
    #include<QNetworkProxy>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        m_tcpServer = new QTcpServer(this);
        m_tcpSocket = new QTcpSocket(this);
        
        //设置代理模式
        m_tcpSocket->setProxy(QNetworkProxy::NoProxy);
    
        //每当有新的连接可用时,就会发出此信号。
        connect(m_tcpServer, &QTcpServer::newConnection, this,[&]{
            m_tcpSocket = m_tcpServer->nextPendingConnection();
            
        //该信号在设备当前读取通道中每次有新的数据可用时发出。
        connect(m_tcpSocket, &QTcpSocket::readyRead, this, &Widget::receiveData);
        
        //当套接字断开连接时发出此信号。
        connect(m_tcpSocket, &QTcpSocket::disconnected,this,[&]{
                ui->textEdit_Rec->append(QStringLiteral("断开连接"));
            });
        });
        
        //获取本地IP
        ui->lineEditI_Ip->setText(QNetworkInterface().allAddresses().at(1).toString());   
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::receiveData()
    {
         //从设备读取所有剩余的数据,并将其作为字节数组返回。
         QByteArray buffer = m_tcpSocket->readAll();
         ui->textEdit_Rec->append(buffer);
    }
    
    void Widget::on_btn_Connect_clicked()
    {
        //服务器监听传入的连接。当port为0时,
        //系统自动选择端口。如果address为QHostAddress::Any,服务器将监听所有网络接口。
        bool ok = m_tcpServer->listen(QHostAddress::Any, ui->lineEdit_Port->text().toInt());
        if(ok)
        {
            ui->btn_Connect->setEnabled(false);
            ui->btn_DisConnect->setEnabled(true);
        }
    }
    
    void Widget::on_btn_DisConnect_clicked()
    {
        //关闭socket
        m_tcpSocket->disconnectFromHost();
        
        //等待直到套接字断开连接。
        //如果连接已经断开,这个函数返回true;否则返回false。
        bool ok = m_tcpSocket->waitForDisconnected(1000);
        
        ui->btn_DisConnect->setEnabled(false);
        ui->btn_Connect->setEnabled(true);
        
        //不再监听端口
        m_tcpServer->close();     
        m_tcpSocket->destroyed();
    }
    
    void Widget::on_btnSend_clicked()
    {
        QString data = ui->textEdit_Send->toPlainText();
        if(data != "")
        {
            //发送数据
            m_tcpSocket->write(data.toLatin1());
        }
    }
    
    
    展开全文
  • 网络编程全套资料 TCP ip协议详解 TCP ip协议详解 TCP ip协议详解 HTTP协议详解 TCP ip协议详解
  • 基于qt的tcp网络编程

    2015-03-22 19:53:45
    是一个word文档,详细讲解了qt的tcp网络编程,文档中有对应的一些代码。
  • 针对Qt中的TCP网络开发进行详细解释,同时有经典实例供学习》
  • qt的tcp网络编程

    2015-06-28 07:20:55
    tcp网络编程,qt调用其库函数的类来实现tcp网络编程通信
  • tcp网络编程

    2012-10-13 13:53:09
    基于c,c++的socke编程,是TCP连接的编程
  • 主要给大家介绍了关于Go语言中TCP/IP网络编程的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • TCP IP网络编程》.pdf

    2018-03-06 11:10:05
    为初学者准备的网络编程! 韩国TCP/IP经典教程!手把手教你套接字编程! 本书涵盖操作系统、系统编程、TCP/IP协议等多种内容,结构清晰、讲解细致、通俗易懂。书中收录丰富示例,详细展现了Linux和Windows平台下套...
  • 利用TCP网络编程,实现一对多的信息交流
  • 用c语言实现简单的socket网络编程TCP/IP客户服务器的连接,并实现两者直接的相互通信
  • TCP介绍及TCP网络编程

    千次阅读 2017-11-12 00:05:50
    一、TCP头部结构: ①16位端口号及16位目的端口号:告知主机该报文段来自哪里(源端口)要传给那个上层协议或应用程序(目的端口)。 ②32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号。...

    一、TCP头部结构:
    这里写图片描述
    ①16位端口号及16位目的端口号:告知主机该报文段来自哪里(源端口)要传给那个上层协议或应用程序(目的端口)。
    ②32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号。A发送给B的第一个报文段中,序号值被系统初始化为某个随机值ISN,后续在该方向上的TCP报文段的序号值被设置为ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
    ③32位确认号:另一方对TCP报文段的响应,其值是收到的TCP报文段的序号值加1。
    ④4位头部长度:标识该TCP头部有多少个32位(4字节,即有多少行)。4位最大能表示15,所以TCP头部最长是60字节。
    ⑤6位标志:
    5.1 URG标志:紧急指针是否有效。
    5.2 ACK标志:确认号是否有效。确认报文段。
    5.3 PSH标志:提示接收端应用程序应该立即从TCP接受缓冲区中读走数据,为接受后续数据腾出空间。TCP缓冲区 在网卡上,有大小限制,如果应用程序一直不读取就会一直在缓冲区,影响后续数据。
    5.4 RST标志:表示要求对方重新建立连接。复位报文段。
    5.5 SYN标志:请求建立连接。同步报文段。
    5.6 FIN标志:通知对方本段要关闭连接。结束报文段。
    ⑥16位窗口大小:TCP流量控制。接收通告窗口告诉对方本端的TCP缓冲区还能接收多大字节的数据,避免发过来接收不了而造成的数据丢失。
    ⑦16位校验和:检验TCP报文段在传输过程中是否损坏。
    ⑧16位紧急指针:紧急数据先处理。

    二、TCP三次握手以及四次挥手(本文都以客户端先发送请求为例)
    这里写图片描述
    三次握手建立连接:
    ①请求连接方(客户端)发送SYN请求连接;
    ②服务器返回SYN/ACK确认收到发送端请求;
    ③客户端回馈给服务器ACK,表示确认收到服务器发送的确认。

    注:1、如果服务器没有收到客户端发送的ACK,则启动超时重传机制,这确保了TCP连接的准确性。
    2、TCP的握手至少有三次,不可再少。
    

    四次挥手断开连接:
    ①请求方(客户端)数据已经发送完毕,向服务器发送FIN请求断开连接;
    ②服务器向客户端发送ACK,对客户端发送的FIN进行确认,并不需要即使断开;
    ③服务器将接收到的数据处理完毕后发送FIN,断开连接;
    ④客户端发送确认消息ACK。

    注:1、应答方(服务器)给在请求方(客户端)发送第一个ACK之后,可能还需要一段时间来处理请求方发送的数据。
    2、如果在应答方(服务器)给请求方(客户端)发送第一个ACK后,服务器可能已经处理完了所有的数据,此时四次挥手可以缩短为三次。
    3、TIME_WAIT状态:出现在主动发起断开链接请求的一端。
    意义:
        3.1、 保证可靠的终止TCP链接
        3.2、 保证迟来的数据报能被识别并丢弃
    

    三、总状态转移图
    这里写图片描述这里写图片描述

    四、TCP编程
    服务器:
    1、创建socket
    2、bind(命名IP地址和端口号)
    3、listen(创建监听队列)
    4、accept(拿到已经完成连接的)
    5、recv/send(收发数据)
    6、close(关闭)

    客户端:
    1、socket
    2、connect
    3、recv/send
    4、close

    ①创建套接字函数:int socket(int domain, int type, int protocol)
    domain:协议簇 IPv4(AF_INET)或 IPv6(AF_INET6) 
    type:选择协议 TCP:SOCK_STREAM     UDP:SOCK_DGRAM   
    protocol:0  
    
    ②命名IP地址和端口号:int bind(int sockfd,const struct sockaddr*addr,int addrlen)
    sockfd:文件描述符 
    addr:指定IP地址和端口号   
    struct sockaddr_in
    {  
        sa_family_t sin_family;   //地址簇:AF_INET
        u_int16_t sin_port;       //端口号(网络字节序:大端模式 PC机:小端模式)
        struct in_addr sin_addr; //IPv4地址结构体
    };  
    struct sin_addr
    {  
           u_int32_t s_addr;        //IPv4地址用网络字节序表示
    };  
    addrlen:该socket地址的长度
    
    ③创建一个监听队列:int listen(int sockfd, int size) 
    size:队列大小,默认值5 。
    
    ④int accept(int sockfd,struct sockaddr *addr ,int *addrlen)
    addr:要连接的服务器的ip地址及端口号(netstat -atp 显示本机上所有的tcp服务程序以及其占用的端口号)
    
    ⑤recv(c,buff,127,0)
    

    代码实现
    ser.c

     void main()
     {
         int sockfd = socket(AF_INET,SOCK_STREAM,0);
         assert(sockfd != -1);
    
         struct sockaddr_in ser,cli;
         ser.sin_family = AF_INET;
         ser.sin_port = htons(6000);
         ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
          int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
          assert(res != -1);
    
          listen(sockfd,5);
    
          while(1)
          {
              int len = sizeof(cli);
              int c = accept(sockfd,(struct sockaddr*)&cli,&len);
              assert(c != -1);
    
              char buff[128] = {0};
              recv(c,buff,127,0);
              printf("recv::%s\n",buff);
              send(c,"I Know",strlen("I Know"),0);
    
              close(c);
          }
          close(sockfd);
      }

    cli.c

    //客户端cli.c
    void main()
     {
         int sockfd = socket(AF_INET,SOCK_STREAN,0);
          assert(sockfd != -1);
    
          struct sockaddr_in ser,cli;
          ser.sin_family = AF_INET;
         ser.sin_port = htons(6000);
          ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
          int res = connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
          assert(res != -1);
    
          send(sockfd,"Hello World",strlen("Hello World"),0);
          char buff[128] = {0};
          recv(sockfd,buff,127,0);
    
          printf("recv::%s\n",buff);
    
          close(sockfd);
      }
    
    展开全文
  • Linux网络编程TCP

    千次阅读 2021-11-03 23:59:08
    文章目录TCP/IP协议网络应用程序常见的两种设计模式Linux Socket 网络编程TCP协议TCP编程服务端API接口socketbindUNIX本地协议族TCP/IP协议族listenaccept客户端API接口connectsend/recv TCP/IP协议 TCP/IP 协议栈是...

    来源:微信公众号「编程学习基地」

    TCP/IP协议

    TCP/IP 协议栈是一系列网络协议(protocol)的总和,是构成网络通信的核心骨架,它定义了电子设备如何连入因特网,以及数据如何在它们之间进行传输。

    OSI 7层模型和TCP/IP四层网络模型对应关系
    请添加图片描述

    计算机网路基础的知识不过多讲解,主要是让大家明白接下来的Linux网络编程数据流属于那一层,具体如下图

    请添加图片描述

    我们接下来讲解的Linux网络编程Tcp协议是属于传输层的协议

    网络应用程序常见的两种设计模式

    C/S模式:
    优点:可以安装在本地, 可以缓存数据, 协议的选择灵活,
    缺点:客户端工具需要有程序员开发, 开发周期长工作量大;
    需要本地安装, 对客户的电脑安全有一定影响.
    B/S模式:
    浏览器/web服务器模式.
    优点:浏览器不用开发, 开发周期短,工作量小
    缺点: 只能选择http协议, 协议选择受限制, 不能缓存数据, 效率受影响.

    Linux Socket 网络编程

    TCP协议

    就记住一句话,TCP是面向连接的可靠的,传输协议。

    TCP编程

    Linux中的网络编程是通过socket接口来进行的。socket是一种特殊的I/O接口,它也是一种文件描述符。常用于不同机器上的进程之间的通信,当然也可以实现本地机器上的进程之间通信。

    使用TCP协议的流程图

    服务端API接口

    socket

    #include <sys/socket.h>
    int socket(int family	//协议簇 一般 AF_INET PF_INET
    	,int type			//套接口类型 SOCK_STREAM(字节流套接口)
    	,int protocol); 	//非原始套接口,参数为 0
    

    套接口类型:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);

    示例:

    listenfd = socket(AF_INET,SOCK_STREAM,0);
    

    bind

    为套接字分配一个本地IP和协议端口

    #include <sys/socket.h>
    int bind(int socket
    , const struct sockaddr *address//协议族地址
    ,socklen_t address_len);	//协议族长度		
    

    - address: 协议族地址,通用的socket地址

    通用的socket地址不是很好用,所以Linux为各个协议族提供了专门的socket地址结构体

    UNIX本地协议族
    struct sockaddr_un {
    	sa_family_t sa_family;
    	char sun_path[100];
    }
    
    TCP/IP协议族

    TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用的socket地址结构体,分别对应IPv4和IPv6

    IPv4对应的协议族sockaddr_in定义如下

    struct sockaddr_in {
    	sa_family_t	sin_family;	/*地址族:AF_INET*/
    	in_port_t	sin_port;	/*网络字节序表示的端口号*/
    	struct in_addr	sin_addr;	/*ipv4地址*/
    }
    
    /* Internet address. */
    struct in_addr {
        uint32_t       s_addr;     /* address in network byte order */
    };
    

    ipv6用的比较少就不单独介绍定义了

    常用的有sockaddr_in(网络地址),sockaddr_un(本地地址), 传入参数时要强制转换为sockaddr*指针类型,示例如下。

    struct sockaddr_in servaddr;
    /*(2) 设置服务器协议族sockaddr_in结构*/
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;//必须和套接字的创建fimile一致
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//表明可接受任意IP地址
    servaddr.sin_port = htons(8888);
    bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    

    listen

    listen函数仅被TCP服务器调用

    #include <sys/socket.h>
    int listen(int sockfd	//socket函数返回的套接口描述字
    	,int backlog); 		//则此值表示listen时的队列大小,最大连接个数
    
    listen(listenfd,2);
    

    accept

    #include <sys/socket.h>         
    int accept(int listenfd			//socket 函数返回的套接口描述字	监听句柄
    	, struct sockaddr *client	//协议族地址
    	, socklen_t * addrlen); 	//客户端 套接字
    
    accept(listenfd , (struct sockaddr *)&cliaddr , &clilen);
    

    客户端API接口

    connect

    #include <sys/socket.h>      
      int connect(int sockfd
      	, const struct sockaddr * addr	//协议族地址
      	, socklen_t addrlen);			//协议族长度		
    
    connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr))
    

    send/recv

    #include <sys/types.h>
    #include < sys/socket.h >         
    ssize_t send(int sockfd, const void *buf, size_t len, int flags); 
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    

    前3个参数与read()相同,参数flags是传输控制标志

    TCP案例

    服务端

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<string.h>
    #include<netinet/in.h>
    #include<netdb.h>
    #include<arpa/inet.h>
    
    #define MAX_BUFF 1024
    #define MAX_LISTEN 10
    
    int main(int argc,char *argv[])
    {
        int defaule_port = 8000;
        int optch = 0;
    	while((optch = getopt(argc, argv, "s:p:")) != -1)
    	{
    		switch (optch)
    		{
            case 'p':
                defaule_port = atoi(optarg);
                printf("port: %s\n", optarg);
                break;
            case '?':
                printf("Unknown option: %c\n",(char)optopt);    
                break;
            default:
                break;
    		}
    	}
    	/*声明服务器地址和客户链接地址*/
    	struct sockaddr_in server_addr,client_addr;
    	socklen_t client_len;
    
    	/*声明服务器监听套接字和客户端链接套接字*/
    	int listen_fd,connect_fd;
    
        /*(1) 初始化监听套接字listenfd*/
    	listen_fd = socket(AF_INET, SOCK_STREAM,0);
    	if(listen_fd == -1)
    	{
    		perror("Socket Error:");
            return 0;
    	}
        
    	/*(2) 设置服务器sockaddr_in结构*/
    	bzero(&server_addr,sizeof(server_addr));
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);    //任意地址
    	server_addr.sin_port = htons(defaule_port);
    
        /*(3) 绑定套接字和端口*/
    	if(bind(listen_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
    	{
    		perror("Bind error:");
            return 0;
    	}
    
        /*(4) 监听客户请求*/
    	if(listen(listen_fd,MAX_LISTEN)==-1)
    	{
    		perror("Listen error:");
            return 0;
    	}
    	
        /*(5) 接受客户请求*/
    	for(;;)
    	{
    		client_len = sizeof(client_addr);
    		connect_fd = accept(listen_fd,(struct sockaddr*)&client_addr,&client_len);
    		if(connect_fd < 0)
    		{
    			perror("accept error");
    			return 0;
    		}
    
    		printf("Connect from %s:%u...\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
            /*声明缓冲区,向客户端发送数据*/
    		char buff[MAX_BUFF] = "hello\n";
    		if(-1 == write(connect_fd,buff,strlen(buff)))
    		{
    			perror("Send error\n");
    			return 0;
    		}
    		printf("Send success...\n");
            /*清空缓冲区,阻塞等待读取客户端发过来的数据*/
    		memset(buff,'\0',sizeof(buff));
    		if(-1 == read(connect_fd,buff,MAX_BUFF))
    		{
    			perror("read error\n");
    			return 0;
    		}
    		write(1,buff,strlen(buff));
    		close(connect_fd);
    	}
    	close(listen_fd);
        return 0;
    }
    

    编译运行,默认8000端口,-p 指定端口

    gcc -o server.c server
    ./server -p 8020
    

    客户端

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    
    const int MAX_LINE = 2048;
    
    int main(int argc ,char**argv)
    {
        /*声明套接字和链接服务器地址*/
        int sockfd;
        int optch,ret = -1;
        const char*server_addr;
        int default_port = 8000;
    
        struct sockaddr_in servaddr;
        /*判断是否为合法输入 必须传入一个参数:服务器Ip*/
        if(argc<3)
        {
            printf("usage:tcpcli <IPaddress>");
            return 0;
        }
        while((optch = getopt(argc, argv, "s:p:")) != -1)
    	{
    		switch (optch)
    		{
            case 's':
                server_addr = optarg;
                break;
            case 'p':
                default_port = atoi(optarg);
                printf("port: %s\n", optarg);
                break;
            case '?':
                printf("Unknown option: %c\n",(char)optopt);    
                break;
            default:
                break;
    		}
    	}
        /*(1) 创建套接字*/
        sockfd =socket(AF_INET,SOCK_STREAM,0);
        if(sockfd==-1)
        {
            perror("socket error");
            return 0;
        }
        /*(2) 设置链接服务器地址结构*/
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family =AF_INET;
        servaddr.sin_port = htons(default_port);
        if(inet_pton(AF_INET , server_addr , &servaddr.sin_addr) < 0)
    	{
    		printf("inet_pton error for %s\n",server_addr);
    		return 0;
    	}
        /*  (3) 发送链接服务器请求  */
        if( (ret = connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr))) < 0)
    	{
    		perror("connect error");
    		return 0;
    	}
        printf("connect seccess,ret:%d..\n",ret);
        struct sockaddr_in c_addr;
        memset(&c_addr, 0, sizeof(c_addr));
        socklen_t len = sizeof(c_addr);
    
        char buf[MAX_LINE];
        while (1)
        {
            /* code */
            memset(buf,'\0',sizeof(buf));
            len = read(sockfd,buf,sizeof(buf)-1);
            if(len == 0)
            {
                printf("server close..\n");
    			return 0;
            }
            printf("recv from server:%s",buf);
    
            memset(buf,'\0',sizeof(buf));
            printf("please enter:\n");
            ssize_t len = read(0,buf,sizeof(buf)-1);
            if(len>0)
            {
                if(strcmp(buf,"quit")==0)
                {
                    printf("quit\n");
                    break;
                }
                buf[len - 1]='\0';
                write(sockfd,buf,strlen(buf));
            }
        }
        close(sockfd);
        return 0;
    }
    

    编译运行,默认8000端口,-s 指定连接的服务器 -p 指定端口

    $ gcc -o client client.c
    $ ./client -s 0.0.0.0 -p 8020
    connect seccess,ret:0..
    recv from server:hello
    please enter:
    hello too
    server close..
    

    简单 tcp服务器和客户端就到这里,下期介绍多线程技术,实现一个多线程的聊天室程序。

    展开全文
  • TCP IP网络编程》尹圣雨+源代码,很适合入门计算机网络编程
  • wince之TCP网络编程

    2013-03-19 21:33:27
    wince之TCP网络编程
  • 网络编程TCP 网络应用程序开发

    千次阅读 2021-11-25 15:56:03
    网络编程TCP 网络应用程序开发TCP 网络应用程序开发流程1. TCP 网络应用程序开发流程的介绍2. TCP 客户端程序开发流程的介绍3. TCP 服务端程序开发流程的介绍4. 小结TCP 客户端程序开发1. 开发 TCP 客户端程序...
  • javaTCP网络编程

    2014-04-03 19:25:53
    java tcp 网络编程,客户端的开发
  • TCP IP网络编程》(韩)尹圣雨_含目录 第一部分主要介绍网络编程基础知识。此部分主要论述Windows和Linux平台网络编程必备基础知识,未过多涉及不同操作系统特性。 第二部分和第三部分与操作系统有关。第二部分主要...
  • 基于ATmeg单片机的TCP/IP协议的网络应用程序开发,具有很高的参考价值

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 262,263
精华内容 104,905
关键字:

tcp网络编程

友情链接: 1.zip