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

【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)

這篇具有很好參考價值的文章主要介紹了【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、引入

UDP和TCP的區(qū)別:

對于TCP協(xié)議有幾個特點:

1?? 傳輸層協(xié)議
2?? 有連接(正式通信前要先建立連接)
3?? 可靠傳輸(在內(nèi)部幫我們做可靠傳輸工作)
4?? 面向字節(jié)流

對于UDP協(xié)議有幾個特點:

1?? 傳輸層協(xié)議
2?? 無連接
3?? 不可靠傳輸
4?? 面向數(shù)據(jù)報

可以看到TCP對比UDP會建立鏈接。

其他的接口跟UDP其實沒什么區(qū)別:【網(wǎng)絡(luò)編程】demo版UDP網(wǎng)絡(luò)服務(wù)器實現(xiàn)

二、服務(wù)端實現(xiàn)

2.1 創(chuàng)建套接字socket

在通信之前要先把網(wǎng)卡文件打開。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

RETURN VALUE
On success, a file descriptor for the new socket is returned.  
On error, -1 is returned, and errno is set appropriately.

這個函數(shù)的作用是打開一個文件,把文件和網(wǎng)卡關(guān)聯(lián)起來。

參數(shù)介紹:

domain:一個域,標(biāo)識了這個套接字的通信類型(網(wǎng)絡(luò)或者本地)。
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
只用關(guān)注上面兩個類,第一個AF_UNIX表示本地通信,而AF_INET表示網(wǎng)絡(luò)通信。
type:套接字提供服務(wù)的類型。
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
這一章我們講的式TCP,所以使用SOCK_STREAM
protocol:想使用的協(xié)議,默認(rèn)為0即可,因為前面的兩個參數(shù)決定了,就已經(jīng)決定了是TCP還是UDP協(xié)議了。

返回值:

成功則返回打開的文件描述符(指向網(wǎng)卡文件),失敗返回-1。

而從這里我們就聯(lián)想到系統(tǒng)中的文件操作,未來各種操作都要通過這個文件描述符,所以在服務(wù)端類中還需要一個成員變量表示文件描述符。

#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include "log.hpp"

class TCPServer
{
static const uint16_t gport = 8080;
public:
    TCPServer(cosnt uint16_t& port = gport)
        : _sock(-1)
        , _port(port)
    {}

    void InitServer()
    {
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd == -1)
        {
            std::cerr << "create socket error" << std::endl;
            exit(1);
        }
        std::cout << "create socket success" << std::endl;
    }

    void start()
    {}
private:
    int _sock;
    uint16_t _port;
};

2.2 綁定bind

#include <sys/socket.h>

int bind(int socket, const struct sockaddr *address,
       socklen_t address_len);

RETURN VALUE
Upon successful completion, bind() shall return 0; 
otherwise, -1 shall be returned and errno set to indicate the error.

參數(shù)介紹:

socket:創(chuàng)建套接字的返回值。
address:通用結(jié)構(gòu)體(【網(wǎng)絡(luò)編程】socket套接字有詳細(xì)介紹)。
address_len:傳入結(jié)構(gòu)體的長度。

所以我們要先定義一個sockaddr_in結(jié)構(gòu)體填充數(shù)據(jù),在傳遞進(jìn)去。
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
然后就是跟UDP一樣,先初始化結(jié)構(gòu)體,再處理IP和端口。
要注意IP要綁定任意IP也就是INADDR_ANY。
至于為什么再上一章【網(wǎng)絡(luò)編程】demo版UDP網(wǎng)絡(luò)服務(wù)器實現(xiàn)有過詳細(xì)講解。

void InitServer()
{
    _sock = socket(AF_INET, SOCK_STREAM, 0);
    if(_sockfd == -1)
    {
        std::cerr << "create socket error" << std::endl;
        exit(1);
    }
    std::cout << "create socket success" << std::endl;
    struct sockaddr_in si;
    // 初始化結(jié)構(gòu)體
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(_port);// 主機轉(zhuǎn)網(wǎng)絡(luò)序列
    si.sin_addr.s_addr = INADDR_ANY;
    if(bind(_sock, (struct sockaddr*)&si, sizeof si) < 0)
    {
        std::cout << "bind socket error" << std::endl;
        exit(1);
    }
    std::cout << "bind socket success" << std::endl;
}

2.3 設(shè)置監(jiān)聽狀態(tài)listen

TCP跟UDP的不同在這里就體現(xiàn)了出來。
要把socket套接字的狀態(tài)設(shè)置為listen狀態(tài)。只有這樣才能一直獲取新鏈接,接收新的鏈接請求。

舉個例子:
我們買東西如果出現(xiàn)了問題會去找客服,如果客服不在那么就回復(fù)不了,所以規(guī)定了客服在工作的時候必須要時刻接收回復(fù)消息,這個客服所處的狀態(tài)就叫做監(jiān)聽狀態(tài)。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int listen(int sockfd, int backlog);

RETURN VALUE
On success, zero is returned.  
On error, -1 is returned, and errno is set appropriately.

關(guān)于第二個參數(shù)backlog后邊講TCP協(xié)議的時候介紹,目前先直接用。

static const int gbacklog = 10;
void InitServer()
{
    _sock = socket(AF_INET, SOCK_STREAM, 0);
    if(_sock == -1)
    {
        std::cerr << "create socket error" << std::endl;
        exit(1);
    }
    std::cout << "create socket success" << std::endl;
    struct sockaddr_in si;
    // 初始化結(jié)構(gòu)體
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(_port);// 主機轉(zhuǎn)網(wǎng)絡(luò)序列
    si.sin_addr.s_addr = INADDR_ANY;
    if(bind(_sock, (struct sockaddr*)&si, sizeof si) < 0)
    {
        std::cout << "bind socket error" << std::endl;
        exit(1);
    }
    std::cout << "bind socket success" << std::endl;
    // 設(shè)置監(jiān)聽狀態(tài)
    if(listen(_sock, gbacklog) < 0)
    {
        std::cout << "listen socket error" << std::endl;
        exit(1);
    }
    std::cout << "listen socket success" << std::endl;
}

2.4 獲取新鏈接accept

上面初始化完畢,現(xiàn)在開始就是要運行服務(wù)端,而TCP不能直接發(fā)數(shù)據(jù),因為它是面向鏈接的,必須要先建立鏈接。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

RETURN VALUE
On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket.  
On error, -1 is returned, and errno is set appropriately.

參數(shù)介紹:

sockfd文件描述符,找到套接字
addr輸入輸出型參數(shù),是一個結(jié)構(gòu)體,用來獲取客戶端的信息。
addrlen輸入輸出型參數(shù),客戶端傳過來的結(jié)構(gòu)體大小。

返回值:

成功返回一個文件描述符
失敗返回-1

而我們知道sockfd本來就是一個文件描述符,那么這個返回的文件描述符是什么呢?
舉個例子:

我們?nèi)コ燥埖臅r候會發(fā)現(xiàn)每個店鋪門口都會有人來招攬顧客,這個人把我們領(lǐng)進(jìn)去門店后,然后他就會繼續(xù)站在門口繼續(xù)招攬顧客,而我們會有里面的服務(wù)員來招待我們,給我們提供服務(wù)。

這里的攬客的人就是sockfd,而服務(wù)員就是返回值的文件描述符。
意思就是sockfd的作用就是把鏈接從底層獲取上來,返回值的作用就是跟客戶端通信。

從這里就知道了成員變量中的_sock并不是通信用的套接字,而是獲取鏈接的套接字。為了方便觀察,我們可以把所有的_sock換_listensock。

void start()
{
    while(1)
    {
        // 獲取新鏈接
        struct sockaddr_in si;
        socklen_t len = sizeof si;
        int sock = accept(_listensock, (struct sockaddr*)&si, &len);
        if(sock < 0)
        {
            // 獲取鏈接失敗無影響,繼續(xù)獲取即可
            std::cout << "accept error, continue" << std::endl;
            continue;
        }
        std::cout << "accept a new link success" << std::endl;
        std::cout << "sock: " << sock << std::endl;
    }
}

2.5 獲取信息與返回信息(文件操作)

上面獲取到了通信用的套接字sock,而因為TCP通信是面向字節(jié)流的,所以后續(xù)通信全部是用文件操作(IO),因為文件也是面向字節(jié)流的。

IO的操作我們可以封裝一個函數(shù)。

void ServerIO(int sock)
{
    char buf[1024];
    // 接收消息       
    while(1)
    {
        ssize_t n = read(sock, buf, sizeof buf - 1);
        if(n > 0)
        {
            buf[n] = '\0';
            std::cout << "read a message: " << buf << std::endl;
            // 把消息發(fā)送回去
            std::string outbuf;
            outbuf += "Server[echo]#";
            outbuf += buf;
            write(sock, outbuf.c_str(), outbuf.size());
        }
        else if(n == 0)
        {
            // 代表客戶端退出
            std::cout << "Client quit" << std::endl;
            break;
        }
    }
}

當(dāng)IO完后要記得關(guān)閉文件描述符sock,不然會導(dǎo)致可用描述符越來越少。
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
驗證發(fā)現(xiàn)可以運行
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)

三、客戶端實現(xiàn)

3.1 創(chuàng)建套接字socket

Socket可以看成在兩個程序進(jìn)行通訊連接中的一個端點,一個程序?qū)⒁欢涡畔懭隨ocket中,該Socket將這段信息發(fā)送給另外一個Socket中,使這段信息能傳送到其他程序中。
所以客戶端也需要一個套接字。

void initClient()
{
    _sock = socket(AF_INET, SOCK_STREAM, 0);
    if(_sock < 0)
    {
        std::cout << "create socket error" << std::endl;
        exit(1);
    }
    std::cout << "create socket success" << std::endl;
}

3.2 綁定問題

跟上一章一樣要綁定,但是不能顯示綁定

3.3 發(fā)起鏈接connect

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
            
RETURN VALUE
If the connection or binding succeeds, zero is returned.  
On error, -1 is returned, and errno is set appropriately.

參數(shù)說明:

這里的addraddrlen填入的是服務(wù)端信息。

在UDP通信中,客戶端在sendto的時候會自動綁定IP和port,TCP這里就是在connect的時候綁定。

void start()
{
    struct sockaddr_in si;
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(_serverport);
    si.sin_addr.s_addr = inet_addr(_serverip.c_str());
    if(connect(_sock, (struct sockaddr*)&si, sizeof si) < 0)
    {
        std::cout << "connect socket error" << std::endl;
    }
    else
    {
        std::string msg;
        while(1)
        {
            std::cout << "Please Enter#";
            std::getline(std::cin, msg);
            write(_sock, msg.c_str(), msg.size());
            // 從服務(wù)端讀取數(shù)據(jù)
            char buf[1024];
            int n = read(_sock, buf, sizeof buf - 1);
            if(n > 0)
            {
                buf[n] = '\0';
                std::cout << buf << std::endl;
            }
            else
            {
                break;
            }
        }
    }
}

最后在析構(gòu)的時候要關(guān)掉文件描述符。

~TCPClient()
{
    if(_sock >= 0)
    {
        close(_sock);
    }  
}

服務(wù)端:
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
客戶端:
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
但此時如果我們在用一個客戶端鏈接,會發(fā)現(xiàn)無法通信,除非第一個客戶端退出。
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
這是因為獲取新鏈接后會進(jìn)入ServerIo中死循環(huán)。只要這個客戶端不退出,就會一直給這個客戶端提供服務(wù)。

那怎么保證多個客戶端并行呢?

3.4 客戶端并行

3.4.1 多進(jìn)程版

因為fork后子進(jìn)程會復(fù)制父進(jìn)程的文件描述符。
這里注意子進(jìn)程并不需要_listensock文件描述符,所以最好關(guān)閉。

pid_t id = fork();
if(id == 0)// child
{
    close(_listensock);
    ServerIO(sock);
    close(sock);
    exit(1);
}

接下來父進(jìn)程怎么辦呢?是等待嗎?
如果父進(jìn)程等待的話又會導(dǎo)致上面的情況,子進(jìn)程不退出父進(jìn)程就一直等待。

子進(jìn)程退出時,會給父進(jìn)程發(fā)送一個SIGCHLD,17號信號。所以有一種解決辦法就是用signal函數(shù),在回調(diào)函數(shù)中把waitpid的參數(shù)設(shè)置為-1(等待任意進(jìn)程),就可以回收。

現(xiàn)在我們不用這種辦法,我們可以這么寫:

pid_t id = fork();
if(id == 0)// child
{
    close(_listensock);
    if(fork() > 0) exit(1);
    ServerIO(sock);
    close(sock);
    exit(1);
}
// father
pid_t ret = waitpid(id, nullptr, 0);
if(ret > 0)
{
    std::cout << "wait success" << ret << std::endl;
}

這里的意思就是創(chuàng)建孫子進(jìn)程,父進(jìn)程直接退出,讓孫子進(jìn)程執(zhí)行ServerIO,此時孫子進(jìn)程就會被操作系統(tǒng)收養(yǎng),不用我們管,而父進(jìn)程退出,外邊的父進(jìn)程也等待成功了。

結(jié)果演示:
客戶端:
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
服務(wù)端:
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
其實下面的等待可以不用等待,因為SIGCHLD信號默認(rèn)的處理方式是忽略。
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)
這里看到客戶端退出了但是文件描述符并沒有被回收。
這里的原因是我們只關(guān)閉了子進(jìn)程的文件描述符,沒有關(guān)閉父進(jìn)程:
【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)

3.4.2 多線程版

struct ThreadData
{
    TCPServer* _self;
    int _sock;
};

void start()
{
    while(1)
    {
        // 獲取新鏈接
        struct sockaddr_in si;
        socklen_t len = sizeof si;
        int sock = accept(_listensock, (struct sockaddr*)&si, &len);
        if(sock < 0)
        {
            // 獲取鏈接失敗無影響,繼續(xù)獲取即可
            std::cout << "accept error, continue" << std::endl;
            continue;
        }
        std::cout << "accept a new link success" << std::endl;
        std::cout << "sock: " << sock << std::endl;
        // 多線程
        pthread_t tid;
        ThreadData* td = new ThreadData({this, sock});
        pthread_create(&tid, nullptr, thread_start, td);
    }
}

static void* thread_start(void* args)
{
    // 線程分離
    pthread_detach(pthread_self());
    ThreadData* tp = static_cast<ThreadData*>(args);
    tp->_self->ServerIO(tp->_sock);
    close(tp->_sock);
    delete tp;
}

3.4.3 線程池版

前面我們寫過線程池【linux】基于單例模式實現(xiàn)線程池,這里直接拿來用即可。
這里就要修改一下代碼,因為ServerIO可以不屬于類,所以可以把ServerIO放在任務(wù)Task.hpp中。

// Task.hpp
#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <cstdio>

void ServerIO(int sock)
{
    char buf[1024];
    // 接收消息       
    while(1)
    {
        ssize_t n = read(sock, buf, sizeof buf - 1);
        if(n > 0)
        {
            buf[n] = '\0';
            std::cout << "read a message: " << buf << std::endl;
            // 把消息發(fā)送回去
            std::string outbuf;
            outbuf += "Server[echo]#";
            outbuf += buf;
            write(sock, outbuf.c_str(), outbuf.size());
        }
        else if(n == 0)
        {
            // 代表客戶端退出
            std::cout << "Client quit" << std::endl;
            break;
        }
    }
}

class Task
{
    typedef std::function<void(int)> func_t;
public:
    Task()
    {}

    Task(int sock, func_t func)
        : _sock(sock)
        , _func(func)
    {}

    void operator()()
    {
        _func(_sock);
    }

    std::string tostringTask()
    {
        char buf[64];
        snprintf(buf, sizeof buf, "%d %c %d = ?", _x, _op, _y);
        return buf;
    }
private:
    int _sock;
    func_t _func;
};

// ThreadPool.hpp
#pragma once

#include <vector>
#include <queue>
#include <mutex>
#include "mythread.hpp"
#include "mymutex.hpp"
#include "Task.hpp"

using std::cout;
using std::endl;

const int N = 5;

template <class T>
class ThreadPool;

template <class T>
struct ThreadData
{
    ThreadPool<T>* _tp;
    std::string _name;
    ThreadData(ThreadPool<T>* tp, const std::string& name)
        : _tp(tp)
        , _name(name)
    {}
};

template <class T>
class ThreadPool
{
private:
    static void* handlerTask(void* args)
    {
        ThreadData<T>* tdp = static_cast<ThreadData<T>*>(args);
        while(true)
        {
            tdp->_tp->lockqueue();
            while(tdp->_tp->isqueueempty())
            {
                tdp->_tp->threadwait();
            }
            T t = tdp->_tp->pop();
            tdp->_tp->unlockqueue();
            t();
        }
        delete tdp;
    }

    void lockqueue() volatile
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlockqueue() volatile
    {
        pthread_mutex_unlock(&_mutex);
    }

    bool isqueueempty() volatile
    {
        return _tasks.empty();
    }

    void threadwait() volatile
    { 
        pthread_cond_wait(&_cond, &_mutex);
    }

    T pop() volatile
    {
        T res = _tasks.front();
        _tasks.pop();
        return res;
    }

    ThreadPool(int num = 5)
        : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        // 創(chuàng)建線程
        for(int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }

    ThreadPool(const ThreadPool<T>& ) = delete;
    ThreadPool<T> operator=(const ThreadPool<T>&) = delete;

public:

    void start() volatile
    {
        for(auto& t : _threads)
        {
            ThreadData<T>* td = new ThreadData<T>(this, t->GetName());
            t->start(handlerTask, td);
        }
    }

    void push(const T& in) volatile
    {
        LockAuto lock(&_mutex);
        _tasks.push(in);
        // 喚醒池中的一個線程
        pthread_cond_signal(&_cond);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for(auto & e : _threads)
        {
            delete e;
        }
    }

    volatile static ThreadPool<T>* GetSingle()
    {
        if(_tp == nullptr)
        {
            _singlelock.lock();
            if(_tp == nullptr)
            {
                _tp = new ThreadPool<T>();
            }
            _singlelock.unlock();
        }
        return _tp;
    }

private:
    int _num;// 線程數(shù)量
    std::vector<Thread*> _threads;
    std::queue<T> _tasks;// 任務(wù)隊列
    pthread_mutex_t _mutex;// 保護任務(wù)隊列
    pthread_cond_t _cond;
    volatile static ThreadPool<T>* _tp;
    static std::mutex _singlelock;
};

template <class T>
volatile ThreadPool<T>* ThreadPool<T>::_tp = nullptr;

template <class T>
std::mutex ThreadPool<T>::_singlelock;

// TCPServer.hpp
#pragma once

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

class TCPServer;
struct ThreadData
{
    TCPServer* _self;
    int _sock;
};

class TCPServer
{
static const uint16_t gport = 8080;
static const int gbacklog = 10;
public:
    TCPServer(const uint16_t& port = gport)
        : _listensock(-1)
        , _port(port)
    {}

    void InitServer()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensock == -1)
        {
            std::cerr << "create socket error" << std::endl;
            exit(1);
        }
        std::cout << "create socket success" << std::endl;
        struct sockaddr_in si;
        // 初始化結(jié)構(gòu)體
        bzero(&si, sizeof si);
        si.sin_family = AF_INET;
        si.sin_port = htons(_port);// 主機轉(zhuǎn)網(wǎng)絡(luò)序列
        si.sin_addr.s_addr = INADDR_ANY;
        if(bind(_listensock, (struct sockaddr*)&si, sizeof si) < 0)
        {
            std::cout << "bind socket error" << std::endl;
            exit(1);
        }
        std::cout << "bind socket success" << std::endl;
        // 設(shè)置監(jiān)聽狀態(tài)
        if(listen(_listensock, gbacklog) < 0)
        {
            std::cout << "listen socket error" << std::endl;
            exit(1);
        }
        std::cout << "listen socket success" << std::endl;
    }

    void start()
    {
        // 線程池初始化
        ThreadPool<Task>::GetSingle()->start();
        while(1)
        {
            // 獲取新鏈接
            struct sockaddr_in si;
            socklen_t len = sizeof si;
            int sock = accept(_listensock, (struct sockaddr*)&si, &len);
            if(sock < 0)
            {
                // 獲取鏈接失敗無影響,繼續(xù)獲取即可
                std::cout << "accept error, continue" << std::endl;
                continue;
            }
            std::cout << "accept a new link success" << std::endl;
            std::cout << "sock: " << sock << std::endl;
            // 線程池
            ThreadPool<Task>::GetSingle()->push(Task(sock, ServerIO));


            // 多線程
            // pthread_t tid;
            // ThreadData* td = new ThreadData({this, sock});
            // pthread_create(&tid, nullptr, thread_start, td);
            
            // 多進(jìn)程
            // pid_t id = fork();
            // if(id == 0)// child
            // {
            //     close(_listensock);
            //     if(fork() > 0) exit(1);
            //     ServerIO(sock);
            //     close(sock);
            //     exit(1);
            // }
            // close(sock);
            // father
            // pid_t ret = waitpid(id, nullptr, 0);
            // if(ret > 0)
            // {
            //     std::cout << "wait success " << ret << std::endl;
            // }


            // ServerIO(sock);
            // // 關(guān)閉使用完的文件描述符
            // close(sock);
        }
    }

    static void* thread_start(void* args)
    {
        // 線程分離
        pthread_detach(pthread_self());
        ThreadData* tp = static_cast<ThreadData*>(args);
        tp->_self->ServerIO(tp->_sock);
        close(tp->_sock);
        delete tp;
    }

private:
    int _listensock;
    uint16_t _port;
};

四、總結(jié)

對比UDP服務(wù)器,TCP服務(wù)器多了獲取新鏈接和監(jiān)聽的操作,而因為TCP是面向字節(jié)流的,所以接收和發(fā)送數(shù)據(jù)都是IO操作,也就是文件操作。文章來源地址http://www.zghlxwxcb.cn/news/detail-462008.html

到了這里,關(guān)于【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • 網(wǎng)絡(luò)編程(8.14)TCP并發(fā)服務(wù)器模型

    網(wǎng)絡(luò)編程(8.14)TCP并發(fā)服務(wù)器模型

    作業(yè): 1.?多線程中的newfd,能否修改成全局,不行,為什么? 2.?多線程中分支線程的newfd能否不另存,直接用指針間接訪問主線程中的newfd,不行,為什么? 多線程并發(fā)服務(wù)器模型原代碼: 1.將newfd改成全局變量效果: ?答:不行,因為newfd是全局變量的話,客戶端連接后生成

    2024年02月13日
    瀏覽(28)
  • 【網(wǎng)絡(luò)編程】實現(xiàn)UDP/TCP客戶端、服務(wù)器

    【網(wǎng)絡(luò)編程】實現(xiàn)UDP/TCP客戶端、服務(wù)器

    需要云服務(wù)器等云產(chǎn)品來學(xué)習(xí)Linux的同學(xué)可以移步/--騰訊云--/--阿里云--/--華為云--/官網(wǎng),輕量型云服務(wù)器低至112元/年,新用戶首次下單享超低折扣。 ? 目錄 一、UDP 1、Linux客戶端、服務(wù)器 1.1udpServer.hpp 1.2udpServer.cc 1.3udpClient.hpp 1.4udpClient.cc 1.5onlineUser.hpp 2、Windows客戶端 二、T

    2024年02月06日
    瀏覽(18)
  • TCP IP網(wǎng)絡(luò)編程(四) 基于TCP的服務(wù)器端、客戶端

    TCP IP網(wǎng)絡(luò)編程(四) 基于TCP的服務(wù)器端、客戶端

    TCP/IP協(xié)議棧 ? TCP/IP協(xié)議棧 TCP/IP協(xié)議棧共分為4層,可以理解為數(shù)據(jù)收發(fā)分成了4個層次化過程。 ? TCP協(xié)議棧 ? UDP協(xié)議棧 鏈路層 鏈路層是物理連接領(lǐng)域標(biāo)準(zhǔn)化的結(jié)果,也是最基本的領(lǐng)域,專門定義LAN、WAN、MAN等網(wǎng)絡(luò)標(biāo)準(zhǔn)。兩臺主機通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)交換,這需要像下圖所示

    2024年01月16日
    瀏覽(18)
  • C#實現(xiàn)簡單TCP服務(wù)器和客戶端網(wǎng)絡(luò)編程

    C#實現(xiàn)簡單TCP服務(wù)器和客戶端網(wǎng)絡(luò)編程

    在C#中進(jìn)行網(wǎng)絡(luò)編程涉及許多類和命名空間,用于創(chuàng)建和管理網(wǎng)絡(luò)連接、傳輸數(shù)據(jù)等。下面是一些主要涉及的類和命名空間: System.Net 命名空間: 這個命名空間提供了大部分網(wǎng)絡(luò)編程所需的類,包括: IPAddress :用于表示IP地址。 IPEndPoint :表示IP地址和端口號的組合。 Socke

    2024年02月11日
    瀏覽(37)
  • 【網(wǎng)絡(luò)編程】實現(xiàn)一個簡單多線程版本TCP服務(wù)器(附源碼)

    【網(wǎng)絡(luò)編程】實現(xiàn)一個簡單多線程版本TCP服務(wù)器(附源碼)

    accept 函數(shù)是在服務(wù)器端用于接受客戶端連接請求的函數(shù),它在監(jiān)聽套接字上等待客戶端的連接,并在有新的連接請求到來時創(chuàng)建一個新的套接字用于與該客戶端通信。 下面是 accept 函數(shù)的詳細(xì)介紹以及各個參數(shù)的意義: sockfd: 是服務(wù)器監(jiān)聽套接字的文件描述符,通常是使用

    2024年02月13日
    瀏覽(30)
  • Python網(wǎng)絡(luò)編程實戰(zhàn):構(gòu)建TCP服務(wù)器與客戶端

    Python網(wǎng)絡(luò)編程實戰(zhàn):構(gòu)建TCP服務(wù)器與客戶端 在信息化時代,網(wǎng)絡(luò)編程是軟件開發(fā)中不可或缺的一部分。Python作為一種功能強大的編程語言,提供了豐富的網(wǎng)絡(luò)編程庫和工具,使得開發(fā)者能夠輕松構(gòu)建各種網(wǎng)絡(luò)應(yīng)用。本文將詳細(xì)介紹如何在Python中進(jìn)行網(wǎng)絡(luò)編程,特別是如何使用

    2024年04月15日
    瀏覽(26)
  • TCP IP網(wǎng)絡(luò)編程(五) 基于TCP的服務(wù)器端、客戶端 (補充)

    TCP IP網(wǎng)絡(luò)編程(五) 基于TCP的服務(wù)器端、客戶端 (補充)

    回聲客戶端出現(xiàn)的問題 在上一節(jié)基于TCP的服務(wù)器端、回聲客戶端中,存在問題: 如果數(shù)據(jù)太大,操作系統(tǒng)就有可能把數(shù)據(jù)分成多個數(shù)據(jù)包發(fā)送到客戶端,客戶端有可能在尚未收到全部數(shù)據(jù)包時就調(diào)用read函數(shù) 問題出在客戶端,而不是服務(wù)器端,先來對比一下客戶端與服務(wù)器端

    2024年02月09日
    瀏覽(37)
  • 《TCP/IP網(wǎng)絡(luò)編程》閱讀筆記--基于TCP的服務(wù)器端/客戶端

    《TCP/IP網(wǎng)絡(luò)編程》閱讀筆記--基于TCP的服務(wù)器端/客戶端

    目錄 1--TCP/IP協(xié)議棧 2--TCP服務(wù)器端默認(rèn)函數(shù)調(diào)用順序 3--TCP客戶端的默認(rèn)函數(shù)調(diào)用順序 4--Linux實現(xiàn)迭代回聲服務(wù)器端/客戶端 5--Windows實現(xiàn)迭代回聲服務(wù)器端/客戶端 6--TCP原理 7--Windows實現(xiàn)計算器服務(wù)器端/客戶端 ????????TCP/IP協(xié)議棧共分 4 層,可以理解為數(shù)據(jù)收發(fā)分成了 4 個層

    2024年02月10日
    瀏覽(29)
  • Socket網(wǎng)絡(luò)編程(TCP/IP)實現(xiàn)服務(wù)器/客戶端通信。

    Socket網(wǎng)絡(luò)編程(TCP/IP)實現(xiàn)服務(wù)器/客戶端通信。

    一.前言 回顧之前進(jìn)程間通信(無名管道,有名管道,消息隊列,共享內(nèi)存,信號,信號量),都是在同一主機由內(nèi)核來完成的通信。 那不同主機間該怎么通信呢? 可以使用Socket編程來實現(xiàn)。 Socket編程可以通過網(wǎng)絡(luò)來實現(xiàn)實現(xiàn)不同主機之間的通訊。 二.Socket編程的網(wǎng)絡(luò)模型如

    2024年02月08日
    瀏覽(36)
  • 【網(wǎng)絡(luò)編程】——基于TCP協(xié)議實現(xiàn)回顯服務(wù)器及客戶端

    【網(wǎng)絡(luò)編程】——基于TCP協(xié)議實現(xiàn)回顯服務(wù)器及客戶端

    個人主頁:兜里有顆棉花糖 歡迎 點贊?? 收藏? 留言? 加關(guān)注??本文由 兜里有顆棉花糖 原創(chuàng) 收錄于專欄【網(wǎng)絡(luò)編程】【Java系列】 本專欄旨在分享學(xué)習(xí)網(wǎng)絡(luò)編程的一點學(xué)習(xí)心得,歡迎大家在評論區(qū)交流討論?? TCP提供的API主要有兩個類 Socket ( 既會給服務(wù)器使用也會給客

    2024年02月03日
    瀏覽(44)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包