socket编程第一日

1、基于TCP协议的网络编程

tcp协议的3次握手
tcp协议的3次握手

2、Qt网络编程类

QTcpSocket:继承自QAbstractSocket,QIODevice,QObject

void QAbstractSocket::connectToHost ( const QString &hostName, quint16 port, OpenMode openMode = ReadWrite )
Attempts to make a connection to hostName on the given port.
The socket is opened in the given openMode and first enters HostLookupState, then performs a host name lookup of hostName. If the lookup succeeds, hostFound() is emitted andQAbstractSocket enters ConnectingState. It then attempts to connect to the address or addresses returned by the lookup. Finally, if a connection is established, QAbstractSocket entersConnectedState and emits connected().
At any point, the socket can emit error() to signal that an error occurred.
hostName may be an IP address in string form (e.g., “43.195.83.32”), or it may be a host name (e.g., “example.com”). QAbstractSocket will do a lookup only if required. port is in native byte order.

客户端进行连接

qint64 QIODevice::write ( constQByteArray & byteArray )
This is an overloaded function.
Writes the content of byteArray to the device. Returns the number of bytes that were actually written, or -1 if an error occurred.

可以在socket中进行写入,会发送到一个缓冲区中等待读取

void QIODevice::readyRead () [signal]
This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
readyRead() is not emitted recursively; if you reenter the event loop or callwaitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).
Note for developers implementing classes derived from QIODevice: you should always emit readyRead() when new data has arrived (do not emit it only because there’s data still to be read in your buffers). Do not emit readyRead() in other conditions.

当socket缓冲区接收到消息,会触发此函数,对缓冲区的数据进行处理

QByteArray QIODevice::readAll ()
This is an overloaded function.
Reads all available data from the device, and returns it as aQByteArray.
This function has no way of reporting errors; returning an empty QByteArray() can mean either that no data was currently available for reading, or that an error occurred.

从socket的缓冲区中,读出所有数据

QTcpServer:

bool QTcpServer::listen ( const QHostAddress& address = QHostAddress::Any, quint16 port = 0 );
Tells the server to listen for incoming connections on address addressand port port. If port is 0, a port is chosen automatically. If address isQHostAddress::Any, the server will listen on all network interfaces.

listen函数能够监听指定地址和指定端口的信息。

void QTcpServer::newConnection() [signal]
This signal is emitted every time a new connection is available.
See also hasPendingConnections() andnextPendingConnection().
Returns true on success; otherwise returns false.
QTcpSocket * QTcpServer::nextPendingConnection() [virtual]
Returns the next pending connection as a connected QTcpSocket object.
The socket is created as a child of the server, which means that it is automatically deleted when theQTcpServer object is destroyed. It is still a good idea to delete the object explicitly when you are done with it, to avoid wasting memory.
0 is returned if this function is called when there are no pending connections.
Note: The returned QTcpSocket object cannot be used from another thread. If you want to use an incoming connection from another thread, you need to override incomingConnection().

一旦TcpServer接收到连接信号,触发自定义的槽函数。在槽函数中使用QTcpServer::nextPendingConnection()得到当前连接的QTcpSocket,之后就可以通过socket进行通信了

3、单服务端多客户端实现方法

根据之前的印象,隐约记得可以使用多进程或者多线程来进行实现,不过这种方式记不太清了,留在之后的日记中具体研究,今天来实现一种不需要多线程多进程的方式。
设计如下,首先启动服务端,之后可以开启多个客户端。
服务端中有一个Socket列表,QList

4、产生了新的问题

对于每次连接生成的socket,如果客户端断开了连接,要把对应的socket在服务端端口删掉,否则在客户端已经关闭的情况下,server中的socket指针已经没有意义了,再对其进行访问程序会出现问题。
暂时没有想到合理的解决方式,留在之后解决。

代码如下:
server.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
namespace Ui {
class Server;
}
class Server : public QMainWindow
{
    Q_OBJECT
public:
    explicit Server(QWidget *parent = 0);
    ~Server();
protected:
    void init();
private slots:
    void on_Btn_send_clicked();
    void slot_newListen();//建立TCP监听
    void slot_acceptConnection();//接受客户端连接
    void slot_displayError(QAbstractSocket::SocketError);
    void revData();
private:
    Ui::Server *ui;
    QTcpServer *tcpServer;
    QList<QTcpSocket *> mySockets;
};
#endif // MAINWINDOW_H

server.cpp

#include "Server.h"
#include "ui_Server.h"

Server::Server(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Server)
{
    ui->setupUi(this);
    init();
}

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

void Server::init()
{
    tcpServer = new QTcpServer;

    slot_newListen();
    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(slot_acceptConnection()));
}

void Server::on_Btn_send_clicked()
{

    int socketCount = mySockets.count();
    for(int i = 0;i<socketCount;i++)
    {
        mySockets[i]->write(ui->Txt_message->text().toLatin1());
    }
}

void Server::slot_newListen()
{
    //监听是否有客户端来访,且对任何来访者监听,端口为6666
    if(!tcpServer->listen(QHostAddress::Any,6666))
    {
        qDebug()<<tcpServer->errorSxtring();
        close();
        return;
    }
}

void Server::slot_acceptConnection()
{
    QTcpSocket *newConnection = tcpServer->nextPendingConnection();
    mySockets.append(newConnection);
    connect(newConnection, SIGNAL(readyRead()),this,SLOT(revData()));//获取套接字后连接信号
    connect(newConnection, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(slot_displayError(QAbstractSocket::SocketError)));
}

void Server::slot_displayError(QAbstractSocket::SocketError)
{
//    qDebug()<<tcpSocket->errorString();
//    tcpSocket->close();
}

void Server::revData()
{
    int socketCount = mySockets.count();
    for(int i = 0;i<socketCount;i++)
    {
        QString datas = mySockets[i]->readAll();

        if(!datas.isEmpty())
            ui->Txt_all->append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+"\n"+datas);
        else
            continue;
        for(int j = 0;j<socketCount;j++)
        {
            if(i == j) continue;//给除了源客户端以外的所有客户端发一份
            mySockets[j]->write(datas.toLatin1());
        }
    }
}

client.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtNetwork>
#include <QtNetwork/QTcpSocket>

namespace Ui {
class Client;
}

class Client : public QMainWindow
{
    Q_OBJECT

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

protected:

   void init();

   void newTCPConnect();//用于建立服务端与客户之间的连接函数

private:

   Ui::Client *ui;

   QTcpSocket *tcpSocket;

private slots:

   void revData(); //接收来自服务端的数据

   void displayError(QAbstractSocket::SocketError);
   void on_pushButton_clicked();
};

#endif // MAINWINDOW_H

client.cpp

#include "Client.h"
#include "ui_Client.h"

Client::Client(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Client)
{
    ui->setupUi(this);
    init();
}

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

void Client::init()
{
    this->tcpSocket = new QTcpSocket(this);



    newTCPConnect();

       //这里我是采用程序启动就自访问服务端(也可根据实际情况采用手动连接手动输入服务端ip的方式。)

    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(revData()));

     //readyRead()表示服务端发送数据过来即发动信号,接着revData()进行处理。

    connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),

           this,SLOT(displayError(QAbstractSocket::SocketError)));
}

void Client::newTCPConnect()
{
    tcpSocket->abort();
    tcpSocket->connectToHost("127.0.0.1",6666);
}

void Client::revData()
{
    QString datas = tcpSocket->readAll();
//    ui->Txt_message->setText(datas);
    ui->Txt_all->append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+"\n"+datas);
}

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

void Client::on_pushButton_clicked()
{
    this->tcpSocket->write(ui->Txt_message->text().toLatin1());
    ui->Txt_all->append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+"\n"+ui->Txt_message->text());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值