Tcp_socket

 Tcp不保证报文完整性(面向字节流)

所以我们需要在应用层指定协议,确保报文完整性

// {json} -> len\r\n{json}\r\n
bool Encode(std::string &message)
{
    if(message.size() == 0) return false;
    std::string package = std::to_string(message.size()) + Sep + message + Sep;
    message = package;
    return true;
}

OSI与TCP/IP

为什么tcp四层协议,IOS七层协议?

虽然IOS七层协议很完善,但实际实现时,有些层次被合并。

将IOS与实际情况相结合
tcp中,把上三层归为 应用层
最后二层归为  网卡 层

OSI七层协议
​​​​​​
TCP四层模型对应OSI
TCP/IP四层模型与OSI对应关系

Tcp与Udp区别

tcp需要握手后建立连接才可以开始服务
使用listen握手

[listen]声明:

       int listen(int sockfd, int backlog);

[accept]声明:

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

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

[connect声明]

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

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


listen返回的fd是给accept使用的,不可用来I/O
我们应对accept 返回的fd进行I/O

Tcp的 accept 返回的fd更像是文件,可以用read读取,因为Tcp是面向字节流
Udp的recvfrom返回的fd不可read等操作,因为Udp不是面向字节流

和Udp一样,Tcp也是全双工通信的(可同时读写)

telnet [域名] 端口号

远程登陆

struct sockaddr

struct sockaddr   基类
struct sockaddr_in,inet,网络间通信
struct sockaddr_un,unix,本机通信
这三个struct前2字节都是16位地址类型(C语言实现多态)


与Tcp协议server交流时,client应先connect,connect成功后才可以write/read
到client与server段开链接时,server中的read会立即返回0,表示与client失去连接

fd & 多进程

子进程继承父进程的文件描述符表。两张,父子各一张(可以理解为子进程浅拷贝父进程fd表,表中fd指向与父进程相同)

父子进程共享的fd中,如果其中一个进程关闭fd,另一个进程中对应fd不受影响,因为fd指向文件具有引用计数,只有引用数为0时,fd指向文件才会真正关闭

将子进程与父进程脱离的方法
signal(SIGCHLD,SIG_IGN),会使系统自动回收子进程
在子进程中生成孙子进程,之后关闭子进程,孙子进程就变为孤儿进程,由系统自动回收。

和进程不同,父子线程共享一张fd表,所以子线程中不用也不能关闭fd。

recv /send  与 read/write

使用recv & send 代替read & write
后者可能导致数据不完整,所以建议前者

[recv]

       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);

[send]

 #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);


tips:

fd是有用的有限的资源,fd周期随进程。如果不关闭fd,会造成fd泄漏

类静态函数成员

类的静态成员函数不能直接访问类的实例成员(非静态成员),但可以访问类的静态成员。
这是因为静态成员函数与类的实力无关(可能使用静态成员函数时,类还没有实例化)

非静态成员函数可以访问静态成员函数(因为后者实例化一定比前者早)


server类中调用pthread_thread_creat时其函数参数的对应函数(void*(void*))不能接收类内普通自定义参数(因为其只能传递server的this),
只能使server类的静态函数成员作为pthread_thread_creat的函数参数。
通过想类的静态函数成员传递包含有线程所需data+server的this指针,在静态成员函数中通过this调用server类的普通成员函数(同时传递data),实现传参。

popen & pclose

封装了pipe 与 fork  /  exec

#include <stdio.h>

       FILE *popen(const char *command, const char *type);

       int pclose(FILE *stream);

板书笔记

TcpEchoShell_code

code/lesson35/1. EchoServer · whb-helloworld/112 - 码云 - 开源中国

 TcpServer.cc

#include "TcpServer.hpp"
#include "CommandExec.hpp"
#include <functional>
#include <memory>


using task_t = function<std::string (std::string)>;
// using namespace 
int main()
{
    ENABLE_CONSOLE_LOG();
    Command cmd;

    task_t task = [&cmd](std::string cmdstr){
        return cmd.Execute(cmdstr);
    };
    std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(task);
    tsvr->InitServer();
    tsvr->Start();
    return 0;
}

TcpServer.hpp

#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include <functional>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

#define BACKLOG 8

// using namespace LogModule;
using namespace ThreadPoolModule;
static const uint16_t gport = 8080;
using handler_t = std::function<std::string(std::string)>;

class TcpServer
{
    using task_t = std::function<void()>;
    struct ThreadData
    {
        int sockfd;
        TcpServer *self;
    };
public:
    TcpServer(handler_t handler, int port = gport):
    _handler(handler), 
    _port(port),
    _isrunning(false)
    {

    }

    void InitServer()
    {
        // 先监听
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if(_listensockfd < 0)
        {
            LOG(LogLevel::FATAL)<<"socket error";
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO)<<"socket create success, _listensocked is "<<_listensockfd;
        // 后bind
        struct sockaddr_in local;// 网络通信用sockaddr_in, 本地用sockaddr_un
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        
        int n = ::bind(_listensockfd, CONV(&local), sizeof(local));
        if(n < 0)
        {
            LOG(LogLevel::FATAL)<<"bind error";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success, sockfd is: "<<_listensockfd;
        // 设置为监听状态
        n = listen(_listensockfd, BACKLOG);
        if(n < 0)
        {
            LOG(LogLevel::FATAL)<<"listen error";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO)<<"listen success, socked is:"<<_listensockfd;
        // 此处可使用::signal(SIGCHLD, SIG_IGN)来将父子进程解绑
    }

    void HandlerRequest(int sockfd)
    {
        LOG(LogLevel::INFO)<<"HandlerRequest, sockfd is:"<<sockfd;
        char inbuffer[4096];
        while(true)
        {
            ssize_t n = recv(sockfd, inbuffer, sizeof inbuffer, 0);
            inbuffer[n] = 0;
            LOG(LogLevel::INFO)<<"server recived:"<<inbuffer;
            if(n > 0)
            {
                inbuffer[n] = 0;
                std::string cmd_result = _handler(inbuffer);// 回调
                ::send(sockfd, cmd_result.c_str(), cmd_result.size(), 0);
                LOG(LogLevel::INFO)<<"server sent:"<<cmd_result;
            }
            else if(n == 0)
            {
                LOG(LogLevel::INFO)<<"client quit"<<sockfd;
                break;
            }
            else
            {
                break;
            }
        }
        ::close(sockfd);// 防止fd泄露
    }

    static void *ThreadEntry(void *args)// 设为静态函数就不用传递this
    {// 当使用多线程(不是封装好的线程池), pthread_thread_create的函数只能接收一个参数
        pthread_detach(pthread_self());
        ThreadData *data = (ThreadData *)args;
        data->self->HandlerRequest(data->sockfd);
        return nullptr;
    }

    void Start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            LOG(LogLevel::DEBUG)<<"accepting...";
            int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);
            if(sockfd < 0)
            {
                LOG(LogLevel::WARNING)<<"accept error:"<<strerror(errno);
                continue;
            }
            // 连接成功
            LOG(LogLevel::INFO)<<"accept success, sockfd is:"<<sockfd;
            InetAddr addr(peer);
            LOG(LogLevel::INFO)<<"client info:"<<addr.Addr();
            // 使用线程池实现
            ThreadPool<task_t>::getInstance()->Equeue([this, sockfd](){
                this->HandlerRequest(sockfd);
            });
        }   
    }

    ~TcpServer()
    {

    }


private:
    int _listensockfd;// 监听socket
    uint16_t _port;
    bool _isrunning;

    // 处理上层任务入口
    handler_t _handler;
};

TcpClient.cc

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

// #include "Common.hpp"

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        std::cout<<"Usage:./client_tcp [server_ip] [server_port]"<<std::endl;
        return 1;
    }
    std::string server_ip = argv[1];
    int server_port = std::stoi(argv[2]);
    int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        std::cout<<"create socket failed"<<std::endl;
        return 2;
    }

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(server_port);
    server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());
    // client 不需要显示进行 bind, tcp是面向连接的, connect底层自动bind
    int n = ::connect(sockfd, (struct sockaddr*)(&server_addr), sizeof(server_addr));
    if(n < 0)
    {
        std::cout<<"connet falied"<<std::endl;
        return 3;
    }
    std::cout<<"connet 成功\n"<<std::endl;
    std::string message;
    while(true)
    {  
        char inbuffer[1024];
        std::cout<<"input message: ";
        std::getline(std::cin, message);
        n = ::write(sockfd, message.c_str(), message.size());
        if(n > 0)
        {
            int m = read(sockfd, inbuffer, sizeof(inbuffer));
            if(m > 0)
            {
                inbuffer[m] = 0;
                std::cout<<inbuffer<<std::endl;
            }
            else break;
        }
        else break;
    }
    ::close(sockfd);
    return 0;
}


CommadExec.hpp

#pragma once 

#include <iostream>
#include <string>

class Command
{
public:
    std::string Execute(std::string cmdstr)
    {
        return std::string("命令执行完毕\n");
    }
};

Common.hpp

#pragma once 
#include <iostream>
#define Die(code) do{exit(code);}while(0)
#define CONV(v) (struct sockaddr *)(v)

enum
{
    USAGE_ERR = 1, 
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR
};

bool SplitString(std::string &in, std::string *key, std::string *val, std::string gap)
{
    size_t pos = in.find(gap);
    if (pos == std::string::npos) return false;
    *key = in.substr(0, pos);
    *val = in.substr(pos + gap.size());
    if(key->empty() || val->empty()) return false;
    return true;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/969325.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

激光工控机在精密制造中的应用与优势

在精密制造中&#xff0c;激光工控机可以用于许多场景例如 激光切割与雕刻&#xff1a;用于金属、塑料、陶瓷等材料的精密切割和雕刻&#xff0c;适用于汽车、航空航天、电子等行业&#xff1b;可实现复杂图案和高精度加工&#xff0c;满足微米级精度要求。 激光焊接&#xf…

基于微型5G网关的石化厂区巡检机器人应用

石化工业属于高风险产业&#xff0c;由于涉及易燃易爆、有毒有害工业原料&#xff0c;为了保障企业的安全生产与持续运营&#xff0c;因此相比其它行业需要进行更高频次、更全面细致的安全巡检和监测。由于传统的人工巡检监测存在诸多不便&#xff0c;例如工作强度大、现场环境…

WebP2P+自研回音消除:视频通话SDK嵌入式EasyRTC构建高交互性音视频应用

随着移动互联网时代的到来&#xff0c;手机端的扬声器大多采用外置设计&#xff0c;且音量较大。在这种情况下&#xff0c;扬声器播放的声音更容易被麦克风捕捉&#xff0c;从而导致回声问题显著加剧。这种设计虽然方便用户在免提模式下使用&#xff0c;但也带来了更复杂的音频…

【STM32】通过HAL库Flash建立FatFS文件系统并配置为USB虚拟U盘MSC

【STM32】通过HAL库Flash建立FatFS文件系统并配置为USB虚拟U盘MSC 在先前 分别介绍了FatFS文件系统和USB虚拟U盘MSC配置 前者通过MCU读写Flash建立文件系统 后者通过MSC连接电脑使其能够被操作 这两者可以合起来 就能够实现同时在MCU、USB中操作Flash的文件系统 【STM32】通过…

飞书专栏-TEE文档

CSDN学院课程连接&#xff1a;https://edu.csdn.net/course/detail/39573

【gRPC-gateway】auth-通过拦截器从上下文中提取元数据用于认证,与从http header转发待认证数据到上下文进行验证,go案例

从grpc上下文中提取元数据用于认证 案例 interceptor.go package serverimport ("context""errors""google.golang.org/grpc""google.golang.org/grpc/metadata""strings" )// UnaryInterceptor 是一个 unary RPC 的拦截器…

win11 终端乱码导致IDE 各种输出也乱码

因为 win11 终端乱码导致IDE 各种输出也乱码导致作者对此十分头大。所以研究了各种方法。 单独设置终端编码对 HKEY_CURRENT_USER\Console 注册表进行修改对 HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processo 注册表进行修改使用命令[Console]::OutputEncoding [Syst…

deepseek+kimi一键生成PPT

1、deepseek生成大纲内容 访问deepseek官方网站&#xff1a;https://www.deepseek.com/ 将你想要编写的PPT内容输入到对话框&#xff0c;点击【蓝色】发送按钮&#xff0c;让deepseek生成内容大纲&#xff0c;并以markdown形式输出。 等待deepseek生成内容完毕后&#xff0c…

从零搭建:Canal实时数据管道打通MySQL与Elasticsearch

Canal实时同步Mysql Binlog至 Elasticsearch 文章目录 Canal实时同步Mysql **Binlog**至**Elasticsearch** 一. 环境准备1.环境检查检查Mysql是否开启BinLog开启Mysql BinlogJava环境检查 2.新建测试库和表3.新建Es索引 二.**部署 Canal Server****2.1 解压安装包****2.2 配置 …

Excel 合并列数据

场景 要求每行数据的每个字段的内容不能以 [2,3,33,22] 形式展示 要求独立成列形式如下 代码 maven 依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.17</version></dependency>…

从零到一:基于Rook构建云原生Ceph存储的全面指南(下)

接上篇&#xff1a;《从零到一&#xff1a;基于Rook构建云原生Ceph存储的全面指南&#xff08;上&#xff09;》 链接: link 六.Rook部署云原生CephFS文件系统 6.1 部署cephfs storageclass cephfs文件系统与RBD服务类似&#xff0c;要想在kubernetes pod里使用cephfs&#…

人工智能之深度学习的革命性突破

深度学习的革命性突破 深度学习是机器学习的一个子领域&#xff0c;通过模拟人脑神经网络的结构和功能&#xff0c;实现对复杂数据的高效处理。近年来&#xff0c;深度学习在计算机视觉、自然语言处理、语音识别等领域取得了革命性突破。本文将深入探讨深度学习的核心架构、突…

C#快速排序QuickSort将递归算法修改为堆栈Stack非递归方式

我们知道,方法的调用是采用Stack的方式[后进先出:LIFO], 在DeepSeek中快速搜索C#快速排序, 搜索结果如图: 我们会发现是采用递归的方式 . 递归的优点: 简单粗暴,类似于直接写数学公式,因代码量较少,易于理解.递归与循环迭代的运行次数都是一致的 递归的缺点: 占用大量的内…

Django开发入门 – 3.用Django创建一个Web项目

Django开发入门 – 3.用Django创建一个Web项目 Build A Web Based Project With Django By JacksonML 本文简要介绍如何利用最新版Python 3.13.2来搭建Django环境&#xff0c;以及创建第一个Django Web应用项目&#xff0c;并能够运行Django Web服务器。 创建该Django项目需…

SQL布尔盲注、时间盲注

一、布尔盲注 布尔盲注&#xff08;Boolean-based Blind SQL Injection&#xff09;是一种SQL注入技术&#xff0c;用于在应用程序不直接显示数据库查询结果的情况下&#xff0c;通过构造特定的SQL查询并根据页面返回的不同结果来推测数据库中的信息。这种方法依赖于SQL查询的…

【Python网络爬虫】爬取网站图片实战

【Python网络爬虫】爬取网站图片实战 Scrapying Images on Website in Action By Jackson@ML *声明:本文简要介绍如何利用Python爬取网站数据图片,仅供学习交流。如涉及敏感图片或者违禁事项,请注意规避;笔者不承担相关责任。 1. 创建Python项目 1) 获取和安装最新版…

【docker知识】快速找出服务器中占用内存较高的容器

本文由Markdown语法编辑器编辑完成。 1.背景&#xff1a; 近期在处理现场问题&#xff0c;观察服务器时&#xff0c;会遇到某些进程占用较高内存的情况。由于我们的服务&#xff0c;基本上都是以容器的方式在运行&#xff0c;因此就需要找到&#xff0c;到底是哪个容器&#…

图数据库neo4j进阶(一):csv文件导入节点及关系

CSV 一、load csv二、neo4j-admin import<一>、导入入口<二>、文件准备<三>、命令详解 一、load csv 在neo4j Browser中使用Cypher语句LOAD CSV,对于数据量比较大的情况,建议先运行create constraint语句来生成约束 create constraint for (s:Student) req…

10. Hbase Compaction命令

一. 什么是Compaction 在 HBase 中&#xff0c;频繁进行数据插入、更新和删除操作会生成许多小的 HFile&#xff0c;当 HFile 数量增多时&#xff0c;会影响HBase的读写性能。此外&#xff0c;垃圾数据的存在也会增加存储需求。因此&#xff0c;定期进行 Compact操作&#xff…

【工业场景】用YOLOv8实现火灾识别

火灾识别任务是工业领域急需关注的重点安全事项,其应用场景和背景意义主要体现在以下几个方面: 应用场景:工业场所:在工厂、仓库等工业场所中,火灾是造成重大财产损失和人员伤亡的主要原因之一。利用火灾识别技术可以及时发现火灾迹象,采取相应的应急措施,保障人员安全和…