TCP/IP 学习笔记(1)- Hello World

原创
2022-12-06
4786
0

本篇文章简单介绍了TCP/IP 通信,服务端使用的是 Linux ,我使用的Linux为 Deepin 国产Linux系统。客户端使用的是 Windows 系统, 界面使用 Qt 完成的,基本的Socket编程使用的是操作系统提供的API。本系列文章所有例程均参考 尹圣雨 著《TCP/IP网络编程》 一书,客户端界面如下图所示:

我的Linux服务器的IP地址为 192.168.150.130 , Server的端口号为 9003
下面是TCP Server的完整代码:

#include <iostream>
#include <QDebug>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <QString>

int main(int argc, char *argv[])
{
    char* portString = "9003";

    int socketHnd = socket(PF_INET, SOCK_STREAM, 0);
    if (socketHnd == -1)
        qDebug() << "Create TCP Socket Error!";

    //std::cout << "Create Socket Successed!";
    qDebug() << "Create Socket Successed!";

    // bind
    sockaddr_in sockInfo;
    memset(&sockInfo, 0, sizeof(sockInfo));
    sockInfo.sin_family = AF_INET;
    sockInfo.sin_addr.s_addr = htonl(INADDR_ANY);
    sockInfo.sin_port = htons(atoi(portString));
    if (bind(socketHnd, (sockaddr*)&sockInfo, sizeof(sockInfo)) == -1)
        qDebug() << "bind Error!";

    // listen
    if (listen(socketHnd, 5) == -1)
        qDebug() << "listion Error!";

    while(1)
    {
        // accept
        socklen_t clentAddrInfoSize = sizeof(sockInfo);
        int clentSocketHnd = accept(socketHnd, (sockaddr*)&sockInfo, &clentAddrInfoSize);
        if (clentSocketHnd == -1)
            qDebug() << "accept error!";

        char pData[50] = {0};
        // Recv Data
        while (recv(clentSocketHnd, pData, sizeof(pData), 0) > 0)
        {
            // Send Data
            send(clentSocketHnd, pData, sizeof(pData), 0);
        }

        close(clentSocketHnd);
    }

    close(socketHnd);

    return 0;
}

服务端的流程如下:

  1. 使用函数 socket() 创建Socket ,socket 是计算机网络通信的工具。它是通过操作系统进行管理的。
  2. 使用函数 bind() ,为创建的 Socket 分配IP地址和端口号。
  3. 使用函数 listen() ,将 Socket 设置为可就受连接的状态。
  4. 使用函数 accept(),接受数据连接请求。
  5. 使用函数 recvsend, 发送和接收数据。

关于每个函数的具体使用,在后续的文章中介绍,下面是客户端部分的完整代码:

头文件

#pragma once

#include <QtWidgets/QWidget>
#include <WinSock2.h>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QTextEdit>
#include <QSpinBox>

class TcpClient : public QWidget
{
    Q_OBJECT

public:
    TcpClient();
    ~TcpClient();

private slots:
    void onClickedConnectButton(void);
    void onClickedSendMessageButton(void);

private:
    SOCKET m_Socket = INVALID_SOCKET;

private:
    QTextEdit *m_TextEdit = nullptr;
    QLineEdit *m_IPLineEdit = nullptr;
    QSpinBox *m_PortEdit = nullptr;
    QPushButton *m_ConnectButton = nullptr;
    QLineEdit *m_SendMessageLineEdit = nullptr;
};

源文件

#include "TCPClient.h"
#include <QVBoxLayout>
#include <QDebug>

TcpClient::TcpClient()
{
    QVBoxLayout *layout = new QVBoxLayout(this);

    // IP Address Layout
    QHBoxLayout *nTopLayout = new QHBoxLayout;
    QLabel *ipTag = new QLabel("Server IP Address:");
    ipTag->setFixedWidth(150);
    ipTag->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
    m_IPLineEdit = new QLineEdit;
    nTopLayout->addWidget(ipTag);
    nTopLayout->addWidget(m_IPLineEdit);

    // Port 
    QHBoxLayout *nPortLayout = new QHBoxLayout;
    QLabel *portTag = new QLabel("Server Port:");
    portTag->setFixedWidth(150);
    portTag->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
    m_PortEdit = new QSpinBox;
    m_PortEdit->setMinimum(1000);
    m_PortEdit->setMaximum(10000);
    m_PortEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
    nPortLayout->addWidget(portTag);
    nPortLayout->addWidget(m_PortEdit);

    // Connect
    m_ConnectButton = new QPushButton("Connect");
    QObject::connect(m_ConnectButton, &QPushButton::clicked, this, &TcpClient::onClickedConnectButton);

    // Send Message
    QHBoxLayout *nSendMessageLayout = new QHBoxLayout;
    QLabel *sendMessageTag = new QLabel("Content:");
    sendMessageTag->setFixedWidth(150);
    sendMessageTag->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
    m_SendMessageLineEdit = new QLineEdit;
    QPushButton *button = new QPushButton("Send");
    nSendMessageLayout->addWidget(sendMessageTag);
    nSendMessageLayout->addWidget(m_SendMessageLineEdit);
    nSendMessageLayout->addWidget(button);
    QObject::connect(button, &QPushButton::clicked, this, &TcpClient::onClickedSendMessageButton);

    // TextEdit
    m_TextEdit = new QTextEdit;
    m_TextEdit->setReadOnly(true);
    
    layout->addLayout(nTopLayout);
    layout->addLayout(nPortLayout);
    layout->addWidget(m_ConnectButton, 0, Qt::AlignRight | Qt::AlignVCenter);
    layout->addLayout(nSendMessageLayout);
    layout->addWidget(m_TextEdit);

    WSADATA data;
    if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
        m_TextEdit->append("Startup Error!");
}

TcpClient::~TcpClient()
{
    if (INVALID_SOCKET != m_Socket)
        closesocket(m_Socket);
    WSACleanup();
}

void TcpClient::onClickedConnectButton(void)
{
    // 断开连接
    if (m_Socket != INVALID_SOCKET)
    {
        closesocket(m_Socket);
        m_Socket = INVALID_SOCKET;

        m_TextEdit->append("Disconnect Server Success!");
        m_ConnectButton->setText("Connect");
        return;
    }

    // 创建Socket
    m_Socket = socket(PF_INET, SOCK_STREAM, 0);
    if (m_Socket == INVALID_SOCKET)
    {
        m_TextEdit->append("Create Socket Fail!");
        return;
    }

    // 连接Server
    SOCKADDR_IN serverSockInfo;
    memset(&serverSockInfo, 0, sizeof(serverSockInfo));
    serverSockInfo.sin_family = AF_INET;
    serverSockInfo.sin_addr.s_addr = inet_addr(m_IPLineEdit->text().toLocal8Bit().data());
    serverSockInfo.sin_port = htons(m_PortEdit->value());

    if (::connect(m_Socket, (SOCKADDR*)&serverSockInfo, sizeof(serverSockInfo)) == SOCKET_ERROR)
    {
        m_TextEdit->append("Connect Server Fail!");
        // 关闭创建的Socket
        closesocket(m_Socket);
        m_Socket = INVALID_SOCKET;
        return;
    }

    m_ConnectButton->setText("DisConnect");
    m_TextEdit->append("Connect Server Success!");
}

void TcpClient::onClickedSendMessageButton(void)
{
    if (m_Socket == INVALID_SOCKET)
        return;

    char* sendData = new char[30];
    memset(sendData, 0, 30);
    strcpy(sendData, m_SendMessageLineEdit->text().toLocal8Bit().data());

    // 发送数据
    int sendLen = send(m_Socket, sendData, 30, 0);
    if (sendLen <= 0)
        return;
    QString sendMessa = "Send: %1";
    sendMessa = sendMessa.arg(m_SendMessageLineEdit->text());
    m_TextEdit->append(sendMessa);

    // 接收数据
    char *pRecvData = new char[50];
    memset(pRecvData, 0, 50);
    QString recvMessage = "Recv: ";
    if (recv(m_Socket, pRecvData, 50, 0) > 0)
        recvMessage += QString::fromLocal8Bit(pRecvData);

    m_TextEdit->append(recvMessage);
    delete[] pRecvData;
}

客户端部分的代码相对简单
首先需要在项目中添加库文件 ws2_32.lib
在使用Socket相关的函数的时候,需要添加头文件 WinSock2.h
windows中的函数跟Linux中的函数基本类似,Windows中的函数就是根据Unix的函数设计的。
函数 WSAStartup()WSACleanup() 为注册Windows网络的函数

客户点中进行Socket通信如下步骤:

  1. 使用函数 socket() 创建socket.
  2. 使用函数 connect() 绑定IP地址和自动分配端口号,并连接主机。
不会飞的纸飞机
扫一扫二维码,了解我的更多动态。

下一篇文章:TCP/IP 学习笔记(2)- 套接字类型与协议设置