我是靠谱客的博主 隐形流沙,最近开发中收集的这篇文章主要介绍【Linux多线程服务端编程】| 【06】TCP网络编程及五个应用示例,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 1 由来
    • 2 TCP网络编程注意事项
    • TCP_SERVER封装
    • 3 TCP简单示例
        • 3.1 discard
        • 3.2 daytime
        • 3.3 time
        • 3.4 echo
        • 3.5 chargen
        • 3.6 总述

1 由来

socket编程时,使用并不难,但容易忽略问题,出现错误,因此封装后以此来降低开发难度;

2 TCP网络编程注意事项

【TCP网络编程最本质的是处理三个半事件】

  • 【1】连接的建立,包括服务端accept新连接和客户端成功发起connect;TCP连接一旦建立,cs是平等的,可以各自收发数据;
  • 【2】连接的断开,包括主动断开close、shutdown和被动断开 read返回0
  • 【3】消息到达,文件描述符可读,是最为重要的一个事件;
  • 【3.5】消息发送完毕,这算半个。低流量的服务,可不关心该事件;“发送完毕”是指将数据写入操作系统的缓冲区,将由TCP协议栈负责数据的发送与重传,不代表对方已经收到数据;

【3.5中有许多细节要注意】

  • 【若要主动关闭连接,如何保证对方已收到全部数据?】
  • 【若应用层有缓冲,如何保证先发送完缓冲区中的数据,在断开连接?】
  • 【若主动发起连接,但对方主动拒绝,如何定期重试?】
  • 【电平触发,需要什么时候关注EPOLLOUT事件?会不会造成busy-loop】
  • 【边沿触发,如何防止漏读造成饥饿?】
  • 【epoll一定比poll快吗】
  • 【在非阻塞网络中,为什么要使用应用层发送缓冲区?若有40Kb数据要发送,但系统的TCP发送缓冲区只剩25K空间,剩下的15KB咋办?】
    • 在等待OS buf可用,回阻塞当前线程,由于不清楚对方何时收到并读取数据,故网络库需要将15KB缓存,放到TCP链接的应用层发送缓冲区,等待socket可写立刻发送数据,该发送操作不会阻塞;若还有数据要发送,则追加到缓冲区;
  • 【非阻塞网络中,为什么用应用层接收缓冲区?若一次读到的数据不够一个完成包,则已读到的包是否需要暂存,等数据都收到在一并处理?】
  • 在非阻塞网络中,如何设计并使用缓冲区?需要考虑减少系统调用,数据一次读多点,且需要考虑内存占用;

TCP_SERVER封装

【主要数据成员】

  • m_socks:存储已连接的套接字;
  • m_worker:工作线程;
  • m_acceptWorker:用于处理连接的线程;
  • m_recvTimeout:接收超时时间;
  • m_name:服务器类型;
  • m_stop:服务是否停止;

【主要成员函数】

  • 构造函数:默认初始化两个工作线程;
  • 析构函数:将所有套接字都关闭,并清空套接字列表,避免引用计数无法释放;
  • bind:提供单个地址或多个地址绑定、监听;
  • start:启动TcpServer开始accept客户端;
  • stop:取消套接字的所有事件、并关闭;
  • handleClient:为虚函数,具体实现交给子类,当连接后,对如何响应客户端,如何处理客户端的请求;
  • startAccept:为虚函数,处理连接客户端;
class TcpServer : public std::enable_shared_from_this<TcpServer> , Noncopyable{
public:
    typedef std::shared_ptr<TcpServer> ptr;
    TcpServer(sylar::IOManager* woker = sylar::IOManager::GetThis(),
                sylar::IOManager* accept_woker = sylar::IOManager::GetThis());
    virtual ~TcpServer();

    virtual bool bind(sylar::Address::ptr addr);
    virtual bool bind(const std::vector<Address::ptr>& addrs
                      ,std::vector<Address::ptr>& fails);
    virtual bool start();
    virtual void stop();

    uint64_t getReadTimeout() const { return m_recvTimeout; }
    std::string getName() const { return m_name; }
    void setReadTimeout(uint64_t v) { m_recvTimeout = v; }
    void setName(const std::string& v) { m_name = v; }

    bool isStop() const { return m_stop; }

protected:
    virtual void handleClient(Socket::ptr client);

    virtual void startAccept(Socket::ptr sock);
private:
    std::vector<Socket::ptr> m_socks;
    IOManager* m_worker;
    IOManager* m_acceptWorker;
    uint64_t m_recvTimeout;
    std::string m_name;
    bool m_stop;
};

3 TCP简单示例

3.1 discard

  • 丢弃所有收到的数据,为最简单的TCP协议;
    在这里插入图片描述

#ifndef __DISCARD_SERVER_H__
#define __DISCARD_SERVER_H__

#include "sylar/tcp_server.h"

namespace sylar {
namespace http {

class Discard: public TcpServer {
public:
    typedef std::shared_ptr<Discard> ptr;

    Discard(sylar::IOManager* worker = sylar::IOManager::GetThis()
               ,sylar::IOManager* accept_worker = sylar::IOManager::GetThis());

protected:
    virtual void handleClient(Socket::ptr client) override;
};

}
}

#endif
=============================cpp=================================

static sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("system");

Discard::Discard(sylar::IOManager* worker, sylar::IOManager* accept_worker)
               : TcpServer(worker, accept_worker){
        setName("discard");
}

void Discard::handleClient(Socket::ptr client) {
    char buf[1024] = {0};
    do {
        int rt = client->recv(buf, 1024);
        SYLAR_LOG_INFO(g_logger) << getName() << " recv size: " << rt;
        if(!client->isConnected() || !strncmp(buf, "q", 1)) {
            SYLAR_LOG_INFO(g_logger) << getName() << "close";
            break;
        }
}

在这里插入图片描述

3.2 daytime

  • 短链接协议,发生完当前时间后,由服务端主动断开连接;
    在这里插入图片描述
class DayTime: public TcpServer {
public:
    typedef std::shared_ptr<DayTime> ptr;

    DayTime(sylar::IOManager* worker = sylar::IOManager::GetThis()
               ,sylar::IOManager* accept_worker = sylar::IOManager::GetThis());

    void getTime();

protected:
    virtual void handleClient(Socket::ptr client) override;

private:
    struct tm m_tm;
    char m_time[128];
};
static sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("system");

DayTime::DayTime(sylar::IOManager* worker, sylar::IOManager* accept_worker)
               : TcpServer(worker, accept_worker){
        setName("daytime Server");
}

void DayTime::getTime() {
    time_t t = time(0);
    localtime_r(&t, &m_tm);
    strftime(m_time, sizeof(m_time), "%Y-%m-%d %H:%M:%S", &m_tm);
    m_time[sizeof(m_time)] = 'n';
}


void DayTime::handleClient(Socket::ptr client) {
    if(client->isConnected()) { 
        getTime();
        client->send(m_time, sizeof(m_time)+1);
        SYLAR_LOG_INFO(g_logger) << getName() << " send msg: " << m_time;
    }
}

在这里插入图片描述

3.3 time

  • 类似daytime,返回32bit整数,发送时间后,关闭连接;
    在这里插入图片描述
    time客户端
    在这里插入图片描述

3.4 echo

  • 双向协议,服务端把客户端的消息返回;
    在这里插入图片描述
  • 当读到数据即发送,避免客户端恶意发送,导致服务端内存暴涨;
void Echo::handleClient(Socket::ptr client) {
    do {
        char buf[1024] = {0};
        int rt = client->recv(buf, sizeof(buf));
        SYLAR_LOG_INFO(g_logger) << getName() << " recv size: " << rt 
                << "content: " << buf;
        if(!client->isConnected() || !strncmp(buf, "q", 1)) {
            SYLAR_LOG_INFO(g_logger) << getName() << "close";
            break;
        }
        client->send(buf, sizeof(buf));
    } while(true);

在这里插入图片描述

3.5 chargen

  • 该协议只发送数据,不接收数据,发送数据不能快于客户端接收速度,还需要用到流量统计功能,定时器功能 ;

在这里插入图片描述


void Chargen::handleClient(Socket::ptr client) {
    char buf[1024] = "Hellon";
    do {
        client->send(buf, sizeof(buf));
        SYLAR_LOG_INFO(g_logger) << getName() << " send msg: " << buf; 
        if(!client->isConnected() || !strncmp(buf, "q", 1)) {
            SYLAR_LOG_INFO(g_logger) << getName() << "close";
            break;
        }
    } while(true);
}

在这里插入图片描述

3.6 总述

  • 以上程序都用到EventLoop,用于注册和分发IO事件;
    在这里插入图片描述
  • 上述使用Reactor模型的复用能力,让一个单线程同时具备多个网络服务功能;

具体源码
查看此处

最后

以上就是隐形流沙为你收集整理的【Linux多线程服务端编程】| 【06】TCP网络编程及五个应用示例的全部内容,希望文章能够帮你解决【Linux多线程服务端编程】| 【06】TCP网络编程及五个应用示例所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部