国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))

這篇具有很好參考價值的文章主要介紹了【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn)),Linux,網(wǎng)絡(luò),linux,tcp/ip

引言

在前一篇文章中,我們詳細(xì)介紹了UDP協(xié)議和TCP協(xié)議的特點(diǎn)以及它們之間的異同點(diǎn)。本文將延續(xù)上文內(nèi)容,重點(diǎn)討論簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn)。通過本文的學(xué)習(xí),讀者將能夠深入了解TCP協(xié)議的實(shí)際應(yīng)用,并掌握如何編寫簡單的TCP網(wǎng)絡(luò)程序。讓我們一起深入探討TCP網(wǎng)絡(luò)程序的實(shí)現(xiàn)細(xì)節(jié),為網(wǎng)絡(luò)編程的學(xué)習(xí)之旅添上一份精彩的實(shí)踐經(jīng)驗(yàn)。

一、TCP協(xié)議

TCP(Transmission Control Protocol)是一種面向連接的通信協(xié)議,它要求在數(shù)據(jù)傳輸前先建立連接,以確保數(shù)據(jù)的可靠傳輸。TCP通過序號、確認(rèn)和重傳等機(jī)制來保證數(shù)據(jù)的完整性和可靠性,同時還實(shí)現(xiàn)了擁塞控制和流量控制,以適應(yīng)不同網(wǎng)絡(luò)環(huán)境下的數(shù)據(jù)傳輸需求。由于TCP的可靠性和穩(wěn)定性,它被廣泛應(yīng)用于網(wǎng)絡(luò)通信中,包括網(wǎng)頁瀏覽、文件傳輸、電子郵件等各種應(yīng)用場景,成為互聯(lián)網(wǎng)協(xié)議套件中的重要組成部分。詳介紹可以看上一篇文章:UDP協(xié)議介紹 | TCP協(xié)議介紹 | UDP 和 TCP 的異同

二、TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn)

接下來,我們打算運(yùn)用線程池技術(shù),模擬實(shí)現(xiàn)一個簡單的TCP網(wǎng)絡(luò)程序。通過充分利用線程池,我們能夠更有效地管理并發(fā)連接,從而提高程序的性能和穩(wěn)定性。這一實(shí)踐將有助于加深我們對網(wǎng)絡(luò)編程關(guān)鍵概念和技術(shù)的理解和掌握。在前文中已經(jīng)提到了線程池,這里就不再贅述其原理和作用。詳細(xì)可以點(diǎn)擊傳送門:?? 線程池

1. 預(yù)備代碼

?ThreadPool.hpp(線程池)

#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>

// 線程信息結(jié)構(gòu)體
struct ThreadInfo
{
    pthread_t tid;  // 線程ID
    std::string name;  // 線程名稱
};

static const int defalutnum = 10;  // 默認(rèn)線程池大小為10

template <class T>
class ThreadPool
{
public:
    void Lock() // 加鎖
    {
        pthread_mutex_lock(&mutex_);
    }
    void Unlock() // 解鎖
    {
        pthread_mutex_unlock(&mutex_);
    }
    void Wakeup() // 喚醒等待中的線程
    {
        pthread_cond_signal(&cond_);
    }
    void ThreadSleep() // 線程休眠
    {
        pthread_cond_wait(&cond_, &mutex_);
    }
    bool IsQueueEmpty() // 判斷任務(wù)隊(duì)列是否為空
    {
        return tasks_.empty();
    }
    std::string GetThreadName(pthread_t tid) // 獲取線程名稱
    {
        for (const auto &ti : threads_)
        {
            if (ti.tid == tid)
                return ti.name;
        }
        return "None";
    }

public:
    static void *HandlerTask(void *args) // 線程任務(wù)處理函數(shù)
    {
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        std::string name = tp->GetThreadName(pthread_self());
        while (true)
        {
            tp->Lock();

            while (tp->IsQueueEmpty()) // 若任務(wù)隊(duì)列為空,則線程等待
            {
                tp->ThreadSleep();
            }
            T t = tp->Pop(); // 從任務(wù)隊(duì)列中取出任務(wù)
            tp->Unlock();

            t(); // 執(zhí)行任務(wù)
        }
    }
    void Start() // 啟動線程池
    {
        int num = threads_.size();
        for (int i = 0; i < num; i++)
        {
            threads_[i].name = "thread-" + std::to_string(i + 1);
            pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this); // 創(chuàng)建線程
        }
    }
    T Pop() // 從任務(wù)隊(duì)列中取出任務(wù)
    {
        T t = tasks_.front();
        tasks_.pop();
        return t;
    }
    void Push(const T &t) // 將任務(wù)推入任務(wù)隊(duì)列
    {
        Lock();
        tasks_.push(t);
        Wakeup();
        Unlock();
    }
    static ThreadPool<T> *GetInstance() // 獲取線程池實(shí)例
    {
        if (nullptr == tp_) // 若線程池實(shí)例為空
        {
            pthread_mutex_lock(&lock_);
            if (nullptr == tp_) // 雙重檢查鎖
            {
                std::cout << "log: singleton create done first!" << std::endl;
                tp_ = new ThreadPool<T>(); // 創(chuàng)建線程池實(shí)例
            }
            pthread_mutex_unlock(&lock_);
        }

        return tp_;
    }

private:
    ThreadPool(int num = defalutnum) : threads_(num) // 構(gòu)造函數(shù)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    ~ThreadPool() // 析構(gòu)函數(shù)
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
    ThreadPool(const ThreadPool<T> &) = delete; // 禁用拷貝構(gòu)造函數(shù)
    const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // 禁用賦值操作符,避免 a=b=c 的寫法
private:
    std::vector<ThreadInfo> threads_; // 線程信息數(shù)組
    std::queue<T> tasks_; // 任務(wù)隊(duì)列

    pthread_mutex_t mutex_; // 互斥鎖
    pthread_cond_t cond_; // 條件變量

    static ThreadPool<T> *tp_; // 線程池實(shí)例指針
    static pthread_mutex_t lock_; // 靜態(tài)互斥鎖
};

template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr; // 初始化線程池實(shí)例指針為nullptr

template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER; // 初始化靜態(tài)互斥鎖

以上代碼實(shí)現(xiàn)了一個簡單的線程池模板類 ThreadPool,其中包含了線程池的基本功能和操作。

  1. 首先定義了一個線程信息結(jié)構(gòu)體 ThreadInfo,用來保存線程的ID和名稱。

  2. 然后定義了一個模板類 ThreadPool,其中包含了線程池的各種操作和屬性:

    • Lock()Unlock() 分別用于加鎖和解鎖。
    • Wakeup() 用于喚醒等待中的線程。
    • ThreadSleep() 用于使線程進(jìn)入休眠狀態(tài)。
    • IsQueueEmpty() 判斷任務(wù)隊(duì)列是否為空。
    • GetThreadName() 根據(jù)線程ID獲取線程名稱。
  3. 定義了靜態(tài)成員函數(shù) HandlerTask,作為線程的任務(wù)處理函數(shù)。在該函數(shù)中,線程會不斷地從任務(wù)隊(duì)列中取出任務(wù)并執(zhí)行。

  4. Start() 函數(shù)用于啟動線程池,創(chuàng)建指定數(shù)量的線程,并將線程的任務(wù)處理函數(shù)設(shè)置為 HandlerTask。

  5. Pop() 函數(shù)用于從任務(wù)隊(duì)列中取出任務(wù)。

  6. Push() 函數(shù)用于將任務(wù)推入任務(wù)隊(duì)列。

  7. GetInstance() 函數(shù)用于獲取線程池的實(shí)例,采用了雙重檢查鎖(Double-Checked Locking)實(shí)現(xiàn)單例模式。

  8. 線程池的構(gòu)造函數(shù)和析構(gòu)函數(shù)分別用于初始化和銷毀互斥鎖和條件變量。

  9. 最后使用靜態(tài)成員變量初始化了線程池實(shí)例指針和靜態(tài)互斥鎖。

?makefile文件

.PHONY:all
all:tcpserverd tcpclient

tcpserverd:Main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
tcpclient:TcpClient.cc
	g++ -o $@ $^ -std=c++11


.PHONY:clean
clean:
	rm -f tcpserverd tcpclient

這段代碼是一個簡單的 Makefile 文件,用于編譯生成兩個可執(zhí)行文件 tcpserverdtcpclient。

  1. .PHONY: all:聲明 all 為一個偽目標(biāo),表示 all 不是一個實(shí)際的文件名,而是一個指定的操作。

  2. all: tcpserverd tcpclient:定義了 all 目標(biāo),它依賴于 tcpserverdtcpclient 目標(biāo)。當(dāng)執(zhí)行 make all 時,會先編譯 tcpserverdtcpclient

  3. tcpserverd: Main.cc:定義了生成 tcpserverd 可執(zhí)行文件的規(guī)則,依賴于 Main.cc 源文件。使用 g++ 編譯器進(jìn)行編譯,指定輸出文件名為 tcpserverd,使用 C++11 標(biāo)準(zhǔn),并鏈接 pthread 庫。

  4. tcpclient: TcpClient.cc:定義了生成 tcpclient 可執(zhí)行文件的規(guī)則,依賴于 TcpClient.cc 源文件。同樣使用 g++ 編譯器進(jìn)行編譯,指定輸出文件名為 tcpclient,使用 C++11 標(biāo)準(zhǔn)。

  5. .PHONY: clean:聲明 clean 為一個偽目標(biāo)。

  6. clean: rm -f tcpserverd tcpclient:定義了 clean 目標(biāo),用于清理生成的可執(zhí)行文件。執(zhí)行 make clean 時將刪除 tcpserverdtcpclient 可執(zhí)行文件。

?打印日志文件

#pragma once

#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define SIZE 1024

#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define Onefile 2
#define Classfile 3

#define LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Screen; // 默認(rèn)打印方式為屏幕輸出
        path = "./log/"; // 默認(rèn)日志文件路徑為當(dāng)前目錄下的"log/"目錄
    }

    void Enable(int method)
    {
        printMethod = method; // 設(shè)置打印方式
    }

    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }

    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
        case Screen:
            std::cout << logtxt << std::endl; // 在屏幕上輸出日志信息
            break;
        case Onefile:
            printOneFile(LogFile, logtxt); // 將日志信息寫入單個文件中
            break;
        case Classfile:
            printClassFile(level, logtxt); // 根據(jù)日志級別將日志信息寫入不同的文件中
            break;
        default:
            break;
        }
    }

    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string _logname = path + logname; // 拼接日志文件路徑
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // 打開或創(chuàng)建一個文件,以追加方式寫入
        if (fd < 0)
            return;
        write(fd, logtxt.c_str(), logtxt.size()); // 將日志信息寫入文件
        close(fd); // 關(guān)閉文件
    }

    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level); // 生成日志文件名,例如"log.txt.Debug/Warning/Fatal"
        printOneFile(filename, logtxt); // 將日志信息寫入對應(yīng)級別的文件中
    }

    ~Log()
    {
    }

    // 重載()運(yùn)算符,用于輸出日志信息
    void operator()(int level, const char *format, ...)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t); // 獲取當(dāng)前時間

        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec); // 格式化左側(cè)部分,包括日志級別和時間信息

        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s); // 格式化右側(cè)部分,即用戶自定義的日志內(nèi)容
        va_end(s);

        // 格式:默認(rèn)部分+自定義部分
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer); // 拼接左右兩側(cè)的日志內(nèi)容
        printLog(level, logtxt); // 打印日志信息
    }

private:
    int printMethod; // 打印方式
    std::string path; // 日志文件路徑
};

這段代碼是一個簡單的日志記錄類 Log,它提供了幾種不同的日志輸出方式和日志級別。

  • #pragma once: 使用編譯器指令,確保頭文件只被編譯一次。

  • 定義了一些常量:

    • SIZE: 緩沖區(qū)大小為 1024。
    • 日志級別常量:Info, Debug, Warning, Error, Fatal。
    • 打印方式常量:Screen, Onefile, Classfile
    • 日志文件名常量:LogFile。
  • Log 類包含以下成員函數(shù)和變量:

    • printMethod: 記錄當(dāng)前的打印方式,默認(rèn)為屏幕輸出。
    • path: 日志文件路徑,默認(rèn)為"./log/"。
  • 構(gòu)造函數(shù) Log() 初始化 printMethodpath

  • Enable(int method): 設(shè)置日志的打印方式。

  • levelToString(int level): 將日志級別轉(zhuǎn)換為對應(yīng)的字符串。

  • printLog(int level, const std::string &logtxt): 根據(jù)打印方式輸出日志信息。

  • printOneFile(const std::string &logname, const std::string &logtxt): 將日志信息寫入單個文件中。

  • printClassFile(int level, const std::string &logtxt): 根據(jù)日志級別將日志信息寫入不同的文件中。

  • 析構(gòu)函數(shù) ~Log()。

  • 重載的函數(shù)調(diào)用運(yùn)算符 operator(): 接受日志級別和格式化字符串,格式化輸出日志信息到不同的輸出位置。

?將當(dāng)前進(jìn)程轉(zhuǎn)變?yōu)槭刈o(hù)進(jìn)程

#pragma once

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string nullfile = "/dev/null"; // 定義空設(shè)備文件路徑

// 將當(dāng)前進(jìn)程變?yōu)槭刈o(hù)進(jìn)程的函數(shù)
void Daemon(const std::string &cwd = "")
{
    // 1. 忽略一些異常信號,以避免對守護(hù)進(jìn)程造成影響
    signal(SIGCLD, SIG_IGN); // 忽略子進(jìn)程結(jié)束信號
    signal(SIGPIPE, SIG_IGN); // 忽略管道破裂信號
    signal(SIGSTOP, SIG_IGN); // 忽略終止信號

    // 2. 創(chuàng)建一個子進(jìn)程并使父進(jìn)程退出,確保守護(hù)進(jìn)程不是進(jìn)程組組長,創(chuàng)建一個新的會話
    if (fork() > 0)
        exit(0); // 父進(jìn)程退出
    setsid(); // 創(chuàng)建新的會話,并成為該會話的首進(jìn)程

    // 3. 更改當(dāng)前調(diào)用進(jìn)程的工作目錄,如果指定了工作目錄則切換到相應(yīng)目錄
    if (!cwd.empty())
        chdir(cwd.c_str()); // 切換工作目錄到指定路徑

    // 4. 將標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤重定向至/dev/null,關(guān)閉不需要的文件描述符
    int fd = open(nullfile.c_str(), O_RDWR); // 打開空設(shè)備文件
    if (fd > 0)
    {
        dup2(fd, 0); // 標(biāo)準(zhǔn)輸入重定向至空設(shè)備
        dup2(fd, 1); // 標(biāo)準(zhǔn)輸出重定向至空設(shè)備
        dup2(fd, 2); // 標(biāo)準(zhǔn)錯誤重定向至空設(shè)備
        close(fd); // 關(guān)閉打開的文件描述符
    }
}

這段代碼實(shí)現(xiàn)了將當(dāng)前進(jìn)程轉(zhuǎn)變?yōu)槭刈o(hù)進(jìn)程的函數(shù) Daemon。

  1. 忽略一些異常信號,避免對守護(hù)進(jìn)程產(chǎn)生影響。
  2. 創(chuàng)建一個子進(jìn)程并使父進(jìn)程退出,確保守護(hù)進(jìn)程不是進(jìn)程組組長,創(chuàng)建一個新的會話。
  3. 更改當(dāng)前調(diào)用進(jìn)程的工作目錄,如果指定了工作目錄,則切換到相應(yīng)目錄。
  4. 將標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤重定向至 /dev/null,即空設(shè)備文件,關(guān)閉不需要的文件描述符,確保守護(hù)進(jìn)程不產(chǎn)生輸出和錯誤信息。

2. TCP 服務(wù)器端實(shí)現(xiàn)(TcpServer.hpp)

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <signal.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 最大連接請求隊(duì)列長度

extern Log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError,
};

class TcpServer;

// 線程數(shù)據(jù)結(jié)構(gòu),用于傳遞給線程處理函數(shù)
class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t): sockfd(fd), clientip(ip), clientport(p), tsvr(t)
    {}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};

// TCP服務(wù)器類
class TcpServer
{
public:
    // 構(gòu)造函數(shù),初始化端口和IP地址
    TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip)
    {
    }

    // 初始化服務(wù)器
    void InitServer()
    {
        // 創(chuàng)建套接字
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "create socket, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, listensock_: %d", listensock_);

        // 設(shè)置套接字選項(xiàng),允許地址重用
        int opt = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));

        // 綁定本地地址
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }

        lg(Info, "bind socket success, listensock_: %d");

        // 監(jiān)聽套接字,開始接受連接請求
        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }

        lg(Info, "listen socket success, listensock_: %d");
    }

    // 啟動服務(wù)器
    void Start()
    {
        // 將當(dāng)前進(jìn)程變?yōu)槭刈o(hù)進(jìn)程
        Daemon();

        // 啟動線程池
        ThreadPool<Task>::GetInstance()->Start();

        lg(Info, "tcpServer is running....");
        
        // 循環(huán)接受客戶端連接并處理
        while(true)
        {
            // 獲取新連接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno)); 
                continue;
            }

            // 獲取客戶端IP和端口
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 打印客戶端連接信息
            lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);

            // 創(chuàng)建任務(wù)對象并加入線程池處理
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

    // 析構(gòu)函數(shù)
    ~TcpServer() {}

private:
    int listensock_;  // 監(jiān)聽套接字
    uint16_t port_;   // 端口號
    std::string ip_;  // IP地址
};

這段代碼是一個簡單的TCP服務(wù)器的實(shí)現(xiàn),包括了創(chuàng)建套接字、綁定地址、監(jiān)聽連接、接受客戶端連接等基本操作。

3. TCP 客戶端實(shí)現(xiàn)(main函數(shù))

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

void Usage(const std::string &proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    // 檢查命令行參數(shù)是否正確
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    // 設(shè)置服務(wù)器地址信息
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));

    while (true)
    {
        int cnt = 5; // 連接重試次數(shù)
        int isreconnect = false; // 是否需要重連
        int sockfd = 0;

        // 創(chuàng)建套接字
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            return 1;
        }

        do
        {
            // 嘗試連接服務(wù)器
            int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                isreconnect = true;
                cnt--;
                std::cerr << "connect error..., reconnect: " << cnt << std::endl;
                sleep(2); // 等待一段時間后重連
            }
            else
            {
                break;
            }
        } while (cnt && isreconnect);

        if (cnt == 0)
        {
            std::cerr << "user offline..." << std::endl;
            break;
        }

        // 與服務(wù)器建立連接后進(jìn)行通信
        while (true)
        {
            std::string message;
            std::cout << "Please Enter# ";
            std::getline(std::cin, message);

            // 向服務(wù)器發(fā)送消息
            int n = write(sockfd, message.c_str(), message.size());
            if (n < 0)
            {
                std::cerr << "write error..." << std::endl;
            }

            // 從服務(wù)器接收消息并顯示
            char inbuffer[4096];
            n = read(sockfd, inbuffer, sizeof(inbuffer));
            if (n > 0)
            {
                inbuffer[n] = 0;
                std::cout << inbuffer << std::endl;
            }
        }

        // 關(guān)閉套接字
        close(sockfd);
    }

    return 0;
}

溫馨提示

感謝您對博主文章的關(guān)注與支持!如果您喜歡這篇文章,可以點(diǎn)贊、評論和分享給您的同學(xué),這將對我提供巨大的鼓勵和支持。另外,我計(jì)劃在未來的更新中持續(xù)探討與本文相關(guān)的內(nèi)容。我會為您帶來更多關(guān)于Linux以及C++編程技術(shù)問題的深入解析、應(yīng)用案例和趣味玩法等。如果感興趣的話可以關(guān)注博主的更新,不要錯過任何精彩內(nèi)容!

再次感謝您的支持和關(guān)注。我們期待與您建立更緊密的互動,共同探索Linux、C++、算法和編程的奧秘。祝您生活愉快,排便順暢!
【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn)),Linux,網(wǎng)絡(luò),linux,tcp/ip文章來源地址http://www.zghlxwxcb.cn/news/detail-850501.html

到了這里,關(guān)于【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字二

    【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字二

    喜歡的點(diǎn)贊,收藏,關(guān)注一下把! TCP和UDP在編程接口上是非常像的,前面我們說過TCP是面向連接的,UDP我們上篇博客也寫過了,我們發(fā)現(xiàn)UDP服務(wù)端客戶端寫好啟動直接就發(fā)消息了沒有建立連接。TCP是建立連接的,注定在寫的時候肯定有寫不一樣的地方。具體怎么不一樣,我們

    2024年04月15日
    瀏覽(101)
  • linux【網(wǎng)絡(luò)編程】之網(wǎng)絡(luò)套接字預(yù)備

    linux【網(wǎng)絡(luò)編程】之網(wǎng)絡(luò)套接字預(yù)備

    在【網(wǎng)絡(luò)基礎(chǔ)】中我們提到了IP地址,接下來了解一下網(wǎng)絡(luò)通信中其他方面的知識 端口號是一個2字節(jié)16位的整數(shù); 端口號用來標(biāo)識一個進(jìn)程, 告訴操作系統(tǒng), 當(dāng)前的這個數(shù)據(jù)要交給哪一個進(jìn)程來處理; 一個端口號只能被一個進(jìn)程占用 通信原理 (公網(wǎng))IP唯一標(biāo)識一臺主機(jī),這樣兩臺

    2024年02月05日
    瀏覽(298)
  • 【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(TCP)

    【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(TCP)

    目錄 地址轉(zhuǎn)換函數(shù) 字符串IP轉(zhuǎn)整數(shù)IP 整數(shù)IP轉(zhuǎn)字符串IP 關(guān)于inet_ntoa 簡單的單執(zhí)行流TCP網(wǎng)絡(luò)程序 TCP socket API 詳解及封裝TCP socket? 服務(wù)端創(chuàng)建套接字? 服務(wù)端綁定? 服務(wù)端監(jiān)聽? 服務(wù)端獲取連接? 服務(wù)端處理請求 客戶端創(chuàng)建套接字 客戶端連接服務(wù)器 客戶端發(fā)起請求 服務(wù)器測試

    2024年03月21日
    瀏覽(113)
  • Linux網(wǎng)絡(luò)編程(二-套接字)

    Linux網(wǎng)絡(luò)編程(二-套接字)

    目錄 一、背景知識 1.1 端口號 1.2 網(wǎng)絡(luò)字節(jié)序 1.3 地址轉(zhuǎn)換函數(shù)? 二、Socket簡介 三、套接字相關(guān)的函數(shù)? 3.1 socket() 3.2 bind() 3.3 connect() 3.4 listen() 3.5 accept()? 3.6 read()/recv()/recvfrom() 3.7 send()/sendto() ?3.8 close() ?四、UPD客服/服務(wù)端實(shí)驗(yàn)? 1.1 端口號 端口號是訪問服務(wù)器的標(biāo)識 ,就好像

    2024年01月22日
    瀏覽(166)
  • 【Linux】網(wǎng)絡(luò)編程套接字一

    【Linux】網(wǎng)絡(luò)編程套接字一

    上篇博客由唐僧的例子我們知道: 在IP數(shù)據(jù)包頭部中,有兩個IP地址,分別叫做源IP地址,和目的IP地址。 思考一下: 不考慮中間的一系列步驟,兩臺主機(jī)我們光有IP地址就可以完成通信了嘛? 想象一下發(fā)qq消息的例子,有了IP地址能夠把消息發(fā)送到對方的機(jī)器上。 但是我們把

    2024年03月26日
    瀏覽(1082)
  • 【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器)

    【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器)

    作者:愛寫代碼的剛子 時間:2024.4.4 前言:本篇博客主要介紹TCP及其服務(wù)器編碼 只介紹基于IPv4的socket網(wǎng)絡(luò)編程,sockaddr_in中的成員struct in_addr sin_addr表示32位 的IP地址 但是我們通常用點(diǎn)分十進(jìn)制的字符串表示IP地址,以下函數(shù)可以在字符串表示和in_addr表示之間轉(zhuǎn)換 字符串轉(zhuǎn)in

    2024年04月14日
    瀏覽(108)
  • Linux網(wǎng)絡(luò)編程——tcp套接字

    Linux網(wǎng)絡(luò)編程——tcp套接字

    本章Gitee倉庫:tcp套接字 客戶端: 客戶端: 關(guān)于構(gòu)造和初始化,可以直接在構(gòu)造的時候,將服務(wù)器初始化,那為什么還要寫到 init 初始化函數(shù)里面呢? 構(gòu)造盡量簡單一點(diǎn),不要做一些“有風(fēng)險”的操作。 tcp 是面向連接的,通信之前要建立連接,服務(wù)器處于等待連接到來的

    2024年02月20日
    瀏覽(96)
  • 【Linux】網(wǎng)絡(luò)基礎(chǔ)+UDP網(wǎng)絡(luò)套接字編程

    【Linux】網(wǎng)絡(luò)基礎(chǔ)+UDP網(wǎng)絡(luò)套接字編程

    只做自己喜歡做的事情,不被社會和時代裹挾著前進(jìn),是一件很奢侈的事。 1. 首先計(jì)算機(jī)是人類設(shè)計(jì)出來提高生產(chǎn)力的工具,而人類的文明綿延至今一定離不開人類之間互相的協(xié)作,既然人類需要協(xié)作以完成更為復(fù)雜的工作和難題,所以計(jì)算機(jī)作為人類的工具自然也一定需要

    2024年02月08日
    瀏覽(89)
  • 【Linux】網(wǎng)絡(luò)---->套接字編程(TCP)

    【Linux】網(wǎng)絡(luò)---->套接字編程(TCP)

    TCP的編程流程:大致可以分為五個過程,分別是準(zhǔn)備過程、連接建立過程、獲取新連接過程、消息收發(fā)過程和斷開過程。 1.準(zhǔn)備過程:服務(wù)端和客戶端需要創(chuàng)建各自的套接字,除此之外服務(wù)端還需要綁定自己的地址信息和進(jìn)行監(jiān)聽。注意:服務(wù)端調(diào)用listen函數(shù)后,處理監(jiān)聽狀

    2024年02月04日
    瀏覽(101)
  • 【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(預(yù)備知識+UDP)

    【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(預(yù)備知識+UDP)

    目錄 預(yù)備知識 1. 理解源IP地址和目的IP地址 2. 理解源MAC地址和目的MAC地址 3. 認(rèn)識端口號 ?4. 理解源端口號和目的端口號 5. 端口號(port) vs 進(jìn)程pid 6. 認(rèn)識TCP協(xié)議和認(rèn)識UDP協(xié)議 7. 網(wǎng)絡(luò)字節(jié)序 socket編程接口? 1. socket 常見API 2. sockaddr結(jié)構(gòu)? 簡單的UDP網(wǎng)絡(luò)程序? 1. 服務(wù)端創(chuàng)建udp

    2024年02月19日
    瀏覽(93)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包