首页 > 系统相关 >Tcp协议讲解与守护进程

Tcp协议讲解与守护进程

时间:2024-10-26 21:16:05浏览次数:7  
标签:std read Tcp nullfd 讲解 进程 include 守护

TCP协议:面向链接,面向字节流,可靠通信

创建tcp_server

1.创建套接字


域:依旧选择AF_INET
连接方式:  


选择SOCK_STREAM 可靠的

2.bind

3.监听装置    
  
      


client要通信,要先建立连接,client主动建立连接,所以服务端要一直等待连接


4.获取连接
  


成功返回新的sockfd,失败返回-1


  
我们在使用UDP时,我们的sockfd一直都是同一个,但是在TCP这里我们收到了一个新的sockfd,那我们客户端服务端通信的时候是用哪一个socket呢?
一个故事:有一家饭店,有一个叫张三的员工在外面拉客,拉到客后就进入餐馆叫服务员A给顾客A提供服务,所以顾客A的所有要求都由服务员A提供。
所以我们在通信时用的是返回的新的socket,我们创建的socket理论上叫Listensocket
如果失败了,返回-1,我们得让循环退出吗?
不,我们的continue让socket持续监听

提供服务
调用read write进行读写
read如果读到了0,表示读到了文件结尾,表明(对端关闭了连接)

#pragma once
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>

#include <unistd.h>
#include <errno.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include "Log.hpp"
#include "Comm.hpp"
using namespace std;
const int defaultblacklog = 5;
class TcpServer
{
public:
    TcpServer(const uint16_t &port) : _port(port)
    {
    }
    void Server(int sockfd)
    {
        while (true)
        {
            char buff[1024];
            int n = read(sockfd, buff, sizeof(buff) - 1);
            if (n > 0)
            {
                //读取成功
                buff[n]=0;
                lg.LogMessage(Info,"read success");
                cout<<"#Client say:"<<buff<<endl;

                // string sendbuff;
                // cout<<"#Please Enter"<<endl;
                // getline(cin,sendbuff);
                // int m=write(sockfd,sendbuff.c_str(),sizeof(sendbuff));
                // if(m<0)
                // {
                //     lg.LogMessage(Fatal,"write failed errno:%d :%s",errno,strerror(errno));
                // }
                // lg.LogMessage(Info,"Write success.......");
            }
            else if(n==0)
            {
                break;
                lg.LogMessage(Info,"Client quit.......");
            }
            else
            {
                lg.LogMessage(Fatal,"read failed errno:%d :%s",errno,strerror(errno));
            }
        }
    }
    void Init()
    {
        // 创建套接字
        _listensocket = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensocket < 0)
        {
            lg.LogMessage(Fatal, "create socket failed errno %d:%s\n", errno, strerror(errno));
        }
        lg.LogMessage(Debug, "create socket success _socket:%d\n", _listensocket);
        // bind

        struct sockaddr_in addr;
        bzero(&addr,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = _port;
        addr.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_listensocket, CONV(&addr), sizeof(addr));
        if (n < 0)
        {
            lg.LogMessage(Fatal, "create socket failed errno %d:%s\n", errno, strerror(errno));
        }
        lg.LogMessage(Debug, "bind socket success _socket:%d\n", _listensocket);
        // 监听装置
        int m = listen(_listensocket, defaultblacklog);
        if (m < 0)
        {
            lg.LogMessage(Fatal, "listen  failed errno %d:%s\n", errno, strerror(errno));
        }
        lg.LogMessage(Debug, "listen success _socket:%d\n", _listensocket);
    }
    void Start()
    {
        // 获取连接
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            int wrsockfd = accept(_listensocket, CONV(&peer), &peerlen);
            if (wrsockfd > 0)
            {
                // 获取连接成功  提供服务
                Server(wrsockfd);
            }
            else
            {
                // 获取连接失败 但是一直获取
                continue;
            }
            // 提供服务
            close(wrsockfd);
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensocket;
};

netstat -nltp 查看服务器
  


客户端


1.创建套接字 (同sever端)


2.建立连接 连接后自动进行bind


  
inet_pton:更安全
  

#include <iostream>
#include <cstring>

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

#include <unistd.h>
#include <errno.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include "Log.hpp"
#include "Comm.hpp"
using namespace std;
void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << "server_ip server_port\n"
              << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        cerr << "Usage error" << endl;
        exit(Usage_Err);
    }

    // 创建套接字
    int _socket = socket(AF_INET, SOCK_STREAM, 0);
    if (_socket < 0)
    {
        lg.LogMessage(Fatal, "create socket failed errno %d:%s\n", errno, strerror(errno));
    }
    lg.LogMessage(Debug, "create socket success _socket:%d\n", _socket);
    // 建立连接

    string serverip=argv[1];
    uint16_t serverport=stoi(argv[2]);
    struct sockaddr_in serveraddr;
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=serverport;
    if(inet_pton(AF_INET,(serverip.c_str()),&serveraddr.sin_addr));

    int n=connect(_socket,CONV(&serveraddr),sizeof(serveraddr));
    if(n<0)
    {
        lg.LogMessage(Fatal, "connect failed errno %d:%s\n", errno, strerror(errno));
    }
    lg.LogMessage(Debug, "connect success _socket:%d\n", _socket);

    // 输入信息
    while(true)
    {
        string buff;
        cout<<"Please Enter:"<<endl;
        getline(cin,buff);
        int n=write(_socket,buff.c_str(),sizeof(buff));
    }
}


  
main,cc

#include "TcpServer.hpp"
#include "Comm.hpp"
#include "Daemon.hpp"
void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << "local_port\n" << std::endl;
}
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        cerr<<"Usage error"<<endl;
        exit(Usage_Err);
    }
    
    uint16_t port=stoi(argv[1]);
    TcpServer *tcps=new TcpServer(port);
    tcps->Init();
    tcps->Start();
    Daemon(true,false);
    lg.Enable(ClassFile);
}

udp面向数据报和tcp面向字节流有什么区别?
数据报:数据和数据是有边界的    sendto一次对应着recv一次
字节流:write 一次十次一百次,read可能一次就读完,也可能几十次,但是read端与write端无关
我们编写IO代码,尤其是网络IO时,我们的read和write是有BUG的

我们的服务器得以后台进程的方式运行,真正的服务,是以后台进程以守护进程(精灵进程)的方式运行

守护进程


进程组ID 会话ID
  


同时启动的进程可以是一个进程组的,进程组id通常是其中的某一个进程的pid
  

每次我们登入linux->OS默认提供1.bash2.提供一个终端->给用户提供命令解释的服务->叫做一个会话组
在一个命令行启动的所有进程,最终都是这个会话内的一个进程组。

会话内的进程组,任何时候,一个会话内部存在很多个进程组,但是默认法人和时刻只允许一个进程组在前台(前台进程组)。

前台进程:可以接受IO的是前台进程、

jobs查看
  
fg task_number 前台
  
ctrl+Z bg task_number 后台  或者进程+&
  

守护进程自己是一个独立的会话,他不属于任何的bash会话。
谁启动setsid,谁就是会话的所属进程。
  
调用setsid的进程不能是组长,启动多个进程时,第一个启动进程就是组长,启动一个进程时,这个进程就是组长
  
所以我们一般要创建子进程,让父进程直接退出,所以守护进程一般都是孤儿进程

创建守护进程


1.忽略可能引起异常退出的信号
例如SIG_CHLD SIG_PIPE


2.让父进程退出


3.让自己成为一个新的会话setsid


4.将进程的CWD更改为/根目录     提高效率
chdir


5.独立的会话组,也就是说没有bash进程了,所以可以将所有文件dup(nullfd,1/0/2)
/dev/null 

tips:1.命名守护进程我们都以d结尾
       2.kill -9
ls /proc/pid -l

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

using namespace std;

const char *rootdic = "/";
const char *nulldic = "/dev/null";
void Daemon(bool isroot, bool isclose)
{
    // 忽略可能引起异常的型号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    // 守护进程不能是组长,所以得创建孤儿进程
    pid_t id = fork();
    if (id != 0)
        exit(0);

    // 创建一个新的会话
    setsid();

    // 根目录
    if (isroot)
    {
        chdir(rootdic);
    }

    if (isclose)
    {
        close(0);
        close(1);
        close(2);
    }
    else
    {
        int nullfd = open(nulldic, O_WRONLY);
        if (nullfd > 0)
        {
            dup2(nullfd, 0);
            dup2(nullfd, 1);
            dup2(nullfd, 2);
            close(nullfd);
        }
    }
}

系统有没有将进程守护化的方法?
daemon
  
将服务器守护化,但企业一般都自己会实现进程守护化

标签:std,read,Tcp,nullfd,讲解,进程,include,守护
From: https://blog.csdn.net/bossface/article/details/143169578

相关文章

  • EMC信号过冲产生的原因:【图文讲解】
    信号在传输的过程中,往往不是标准的矩形波信号,尤其在高速信号中,保证信号的完整性是十分重要的,影响信号完整性最主要的因素之一,就是阻抗不匹配,通常表现在传输线上,而阻抗不匹配直接导致信号的反射,反射信号与原始信号叠加,就会产生过冲、回沟、台阶等信号完整性问题。1:什么是过冲......
  • 一篇最全Python 爬虫超详细讲解(零基础入门,适合小白)
    爬虫是指通过编程自动从网页上获取信息的技术.想象你平时打开网页,右键点击“查看源代码”,那些你看到的HTML代码就是网页的结构,而爬虫就像一位帮你阅读这些网页内容的“机器人”.创作不易,还请各位同学三连点赞!!收藏!!转发!!!刚入门学习Python的小伙伴可以试试我的这份学习方法......
  • 基于Vue+NodeJS+express的预约上门维修服务运营与数据分析系统(源码+node+vue+部署文
    收藏关注不迷路!!......
  • 仓储管理系统(源码+文档+部署+讲解)
    仓储管理系统、智能仓储管理平台、库存优化控制系统、仓库自动化管理系统、库存智能调配系统、仓储资源规划系统、智能库存监控系统、仓库作业执行系统、库存管理与分析系统、仓库管理智能平台、库存流转跟踪系统、智能供应链管理平台供应商本文将深入解析“仓储管理系统”的项目,探......
  • Redis的详细安装教程和环境变量配置(附有详细步骤讲解及相关操作截图和代码)
    NoSQL简介NoSQL数据库是一种非关系型数据库,它在处理大规模、高并发的动态网站数据时具有明显优势。NoSQL数据库的出现是为了解决传统关系数据库在处理大数据量和高并发请求时遇到的性能瓶颈。NoSQL数据库的设计允许它们在分布式环境中更有效地扩展,同时提供灵活的数据模型来适应不......
  • C语言-详细讲解-洛谷P1255 数楼梯
    目录1.题目要求2.题目解读 1.如何计算走法数?2.如何解决大数加法,防止数据溢出1.进位的处理2.正序运算,倒序输出3.寻找结果中最高的非零位3.代码实现1.题目要求2.题目解读 一道非常经典的题目,简洁易懂,但需要一定的数学思维,难点如下:1.如何计算走法数?这里需要我......
  • 网络协议基础(2):socket套接字及TCP、UDP的实现
    socket套接字及TCP、UDP的实现socket套接字socket的基本概念socket的类型Socket的工作流程Socket的编程接口(C++示例)1.创建Socket2.绑定地址3.监听连接4.接受连接5.连接到服务器6.发送数据7.接收数据8.关闭Socketsocket相关的结构体sockaddr结构体sockaddr......
  • 中电金信:守护金融安全·内控合规与反电信诈骗
    ​在金融科技迅猛发展的今天,金融机构在享受数字化带来的便捷的同时,也面临着日益严峻的内控合规挑战和电信诈骗威胁。监管政策的日益严格和电信诈骗手段的不断翻新,对金融机构的风控能力提出了更高的要求。  2023年以来,国家金融监督管理总局、央行和外汇管理局共开出罚单3324......
  • 基于SpringBoot+Vue的学前教育资源共享平台系统网站(源码+LW+调试文档+讲解)
    背景及意义这个题目是关于基于SpringBoot和Vue的学前教育资源共享平台系统网站。该平台旨在为学前教育领域提供一个便捷的资源共享和交流的场所。SpringBoot作为后端框架,能够确保系统的高效运行和稳定性。它可以处理大量的用户请求,管理资源的上传、下载和存储等操作......
  • 基于SpringBoot+Vue的学生成长系统(源码+LW+调试文档+讲解)
    背景及意义这个题目是关于基于SpringBoot和Vue的学生成长系统。该系统旨在全面记录和促进学生的成长发展。SpringBoot作为后端技术,提供了稳定可靠的服务支持和高效的数据处理能力。它可以管理学生的各种信息,如学习成绩、课外活动参与情况、品德表现等。Vue则用于构......