网络编程,tcp,守护进程化,前后台任务,bash与shell,会话

news2024/9/28 6:07:45

上篇,我们讲解了udp服务器与客户端的功能,这篇我们将使用tcp协议来进行编程;tcp服务器相比较与udp要更加稳定与安全,tcp服务器是面向连接的数据传输;

1. tcp服务器与客户端

下面是我实现的完整代码可以辅助下面的讲解理解:

network_code/socket_2024_9_17/4_tcp · future/Linux - 码云 - 开源中国 (gitee.com)

下面我将对其值得注意的地方进行讲解;

1.1 socket套接字接口

socket与bind

由于这两个接口在上一篇就有讲过了,所以这里只是说明一下,证明他们的使用;

 1.1.1 listen

这个函数是服务器用来监听socket套接字的接口,socket套接字通过上面的socket函数创建,socket函数创建好socket套接字之后,我们需要将这个绑定了本进程信息的套接字通过listen函数设置为listensock套接字,设置成功后这个listen函数会返回一个listensock,这个listensock负责监听底层有无其他进程向服务器进程进行访问,如果有进程通过网络来访问服务器进程,由于tcp协议是面向连接的,在底层中服务端会向客户端回信标识自己接收到了访问,这个listensock就是在底层进行回信的套接字,但是我们是不清楚他内部是如何实现的;

上面大致的讲解了listen接口做了什么下面我们来讲讲这个接口的使用;

1.这个接口的返回值是一个套接字也可以看作是封装了sock的listensock网卡文件接口,之后服务器的工作就是通过listensock来实现的,当listen失败时会返回-1,且设置错误码errno;

2.函数的第一个参数是我们使用socket函数创建的本线程的socket套接字;

3.函数的第二个参数是一个回文,这个回文暂时不做讲解,现在只需要知道数值大小不需要设置太大,设置在5到10之间即可 

1.1.2 connect

这个函数一般是客户端用来连接服务器的,因为服务器一般是被动接收很多不同客户端发送来的信息的,所以需要客户端先发送连接请求,而tcp协议在通信是是需要建立连接的(udp前面的操作就是直接通信,没有建立连接),而这个connect函数就是用来建立连接的函数,我们知道上面的listen函数设置了套接字来监听有什么客户端进程对服务器发起了访问;connnet函数就如同发起访问的函数一般,告诉服务器,我要来访问你了,你收到的话,请给我回信告诉我你收到了,如果客户端收到服务端接收成功的回信,connect就会返回0,说明建立连接成功,可以向服务端发送信息了,此时connect的底层其实也会发送信息给服务端,告诉服务端我收到了你的回信,但底层的实现我们也还是不清楚的; 

接下来我们继续介绍函数的使用;

1.connect的返回值建立连接成功返回0失败返回-1并设置errno错误码

2.connect的第一个参数是客户端所创建的套接字

3.connect的第二个和第三个参数是要访问的服务器的ip地址与端口号 

 1.1.3 accept

上面的两个函数都是服务器用来接收客户端回信的函数,accept4功能比accept多一个flag的设置功能,我们暂时先不管他即可;上面说到connect函数向服务器回信告诉服务器连接已经建立成功我们可以开始通信了,此时由于listensock是用来监听访问的,无法再为客户端的请求服务,所以需要通过accept函数来接收客户端回信,还要分配新的socket来对客户端的请求服务;accept的底层会创建出新的套接字作为返回值,提供给程序员; 

1.accept的返回值是一个sockfd这个sockfd是用来为客户端提供服务的sock,我们可以从这个网卡文件描述符中获取客户端发来的信息,也可以向其中返回我们的数据,和普通文件描述符一般使用即可,返回值为-1表示接收出现错误,并设置errno;

2.accept的第1个参数是监听套接字,用来获取监听到的来访问的客户端

3.accept的第2和第3个参数是输出型参数可以获取访问的客户端的信息的sockaddr结构体与结构体大小;

 1.2 telnet连接命令

通过上面的接口介绍,我们再联系我代码的实现就可以基本的写出tcp客户端和服务器的大致功能,接下来呢介绍一个linux下现成的工具telnet,使用方法:

       telnet  (ip地址)  (端口号)

例:telnet    127.0.0.1     10000

 这个小工具可以完成对服务器发送信息,并接收服务器返回数据的功能;

1.3 发送信息转换网络字节序转换

我们再bind和获取sockaddr信息时都需要将数据进行网络字节序的转换,那为什么我们发送到网络中的信息不需要进行网络字节序的转换呢?其实我们不需要纠结这些,既然我们能够做到信息的成功传输那么就说明,再socket套接字的底层一定是对信息做了处理,使得信息在网络中的传输是符合网络字节序的;而例如sin_addr和sin_port这样的信息是我们再用户层写的,要传入内核中的数据,这样的特例需要我们进行显式的修改而已;

2.tcp服务器服务实现方式

我们对tcp服务器有几种不同的实现方式:

            1.单进程,无法做到同时为多个客户端服务
             serverce(socketfd, userIp, userPort);
             close(socketfd);

            2.多进程
            pid_t pid = fork();
            if (pid == 0)
            {
                // 子进程
                close(_socketListen);
                if (fork() != 0)
                    exit(-1);
                serverce(socketfd, userIp, userPort);
                close(socketfd);
                exit(-1); // 孙子进程也要记得退出哦
            }
             父进程
            close(socketfd);
            waitpid(pid, nullptr, 0);

            3.多进程 使用signal函数进行等待,父进程不需要阻塞等待
            pid_t pid = fork();
            if (pid == 0)
            {
                // 子进程
                close(_socketListen);
                serverce(socketfd, userIp, userPort);
                close(socketfd);
                exit(-1);//子进程要记得退出哦
            }
            // 父进程
            close(socketfd);

            4.进程池,在run函数最开始就创建多个进程,进程竞争的接收
            客户端,但是需要注意对与socket的accept需要加锁访问

            5.多线程
            pthread_t tid;
            threadData* data= new threadData(socketfd, userIp, userPort, this);
            pthread_create(&tid, 0, routine, (void *)data);

             6.线程池
            task t = task(socketfd, userIp, userPort);
            threadPool<task>::getThreadPool()->push(t);

这些实现方式是层层递进的,我们一开始使用的是单进程方式,但是由于tcp是面向连接的连接的用户,一个进程只能服务一个客户端所以,这样的服务器是不合格的;——>转变为了多进程的服务,但是虽然多进程服务,进程创建的消耗太大了;——>转变为了多线程服务,多线程消耗小,但是线程的创建也是有代价的;——>最后将服务设计成多线程的服务,并且限制线程数量,用线程池来跑,再把服务修改为短服务,这样使得服务器可以长久的运行;如何实现可以通过上面的代码来了解,这里就不多做讲解了;

 3.tcp客户端的断连重连功能

我们将服务器的功能基本完善后,由于将服务器设置为了短服务,所以我们的客户端想要连接服务器需要不断重新accept去连接服务器,那么这样的过程我们还可以丰富一下再加上重复连接的功能,使得客户端的模型更加贴近我们现实生活中的场景:

会让服务器的功能大致成为上面这样;

我们可以看看我们服务器的重连现象:

我们只需要看到,我们是可以实现重连功能的,至于我上面实现的是什么样的服务可以先不用了解,上面实现的服务一个翻译功能,我们输入英文,其可以翻译中文给我们;在我们打王者荣耀的时候,其实我们掉线了其实也是有这个重连功能的:

其实重连功能基本都是这样哒!

3.1 setcocketopt接口

我们实现的服务器在重新启动时有可能会存在端口号被占用无法马上重新启动服务器的情况这个时候,我们只需要使用这个接口设置一下即可解决此问题:

// 下面的代码用来让服务器重启不需要等待时间,暂时不知道原理,先用着
        int opt = 1;
        setsockopt(_socketListen, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

4.tcp服务器翻译功能的实现

 下面是代码实现,其实在我上面的gitee链接中也有,这里拿出来更方便观看

#pragma once
#include <iostream>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "log.hpp"
#include <cstring>
#include <unistd.h>
#include <unordered_map>
#include <fstream>

class dict
{
public:
    void Init()
    {
        ifstream in("./dict.txt");
        string str;
        while (getline(in, str))
        {
            string key, value;
            auto pos = str.find(':');
            if (pos == string::npos)
                continue;
            key = str.substr(0, pos);
            value = str.substr(pos + 1);
            _dict.insert({key, value});
        }
    }

    static dict *getdict()
    {
        // 这里因为是只读,不会对其修改,所以不需要上锁
        if (_self == nullptr)
        {
            _self = new dict;
        }
        return _self;
    }

    unordered_map<string, string> _dict;

private:
    dict() {}
    dict(const dict &) = delete;
    dict &operator=(const dict &) = delete;

    static dict *_self;
};

dict* dict::_self = nullptr;

dict.txt文件:

yellow:黄色
red:红色
pig:猪
cat:猫
dog:狗

通过将一个字典载入服务器中,让服务器可以通过这个字典的键值对获取相应的翻译:

            for(auto word:dict::getdict()->_dict)
            {
                if(word.first==getMsg)
                    sendInfo+=word.second;
            }
            if(sendInfo.size()==0)
                sendInfo="unkonw";

5.守护进程化

 在我们现实生活中,我们使用的app如王者荣耀,抖音,这些app无论我们什么时候,我们只要联网了就可以使用这些软件,所以这些软件提供的服务都是24小时的,那么他们的服务器也一定是24小时运行的;而如果想要一个进程每天24小时运行,那么这个进程一定不能被误杀并且不能出现让进程死亡的bug,那么我们怎么样才能做到呢?接下来我们就通过守护进程化来做到;首先我们先讲解一下,前后台任务的概念:

5.1 前台后台任务

5.1.1 什么是会话与前后台任务与bash和shell

首先我们需要介绍一个名词——会话,在我们使用电脑时,电脑也可以创建多个用户,每个不同的用户登录上电脑就会生成不同的会话。而我们云服务器在登录远程主机时每次登录都会创建一个会话,我们可以这样理解:

对于我们的会话,我们可以这么理解,会话是一个用户在使用主机时所产生的与主机交互的一个实体,我们运行的进程还有需要的数据都会显示在会话上,而大多数时刻一个会话在一个时刻是只能显示一个页面的,那么在这个页面上显示的就是我们的前台进程(任务),所以一个会话只能在一个时刻存在一个前台进程(任务)

而在我们的linux云服务器上我们可以通过xshell对其进行远程连接,我们每建立一次连接,就是在远程的云服务器主机上建立了一个会话session,而这些每个不同的会话都会有他们自己的唯一的前台程序(任务),而bash就是linux下的一个默认的前台程序(任务),他会将我们标准输入(键盘)上的数据进行解析,运行出我们想要执行的指令;

Bash(全称为 "Bourne Again SHell")是一个命令行解释器;

shell是用来与操作系统进行交互的接口,他有bash还有图形化界面多种表示方式

我们下面使用grep查看也可以查看到bash默认前台任务: 

那么什么样的进程(任务)会被称前台程序(任务)呢?

我们可以将拥有标准输入流的进程当作前台程序,在windows这样带有图形化界面的程序上不仅仅是键盘还有鼠标拥有这些输入的就是前台程序(任务); 

5.1.2 前后台任务切换指令 

我们理解了什么是前后台任务后,在linux下我们可以使用一些命令让我们的任务进行前后台的切换:

1.将前台任务置换为后台任务指令:

./进程名 &   例如:  ./a.out &

我们有一个这样的程序:

当我们使用&运行它时:

这个程序这样运行我们无法使用ctrl+将其终止,因为我们ctrl+c会发送信号给前台进程,而这里的test是后台进程了,所以也证明了只有前台程序才拥有标准输入流(键盘);

2.查看后台程进程令命令

jobs

3.将后台进程提到前台命令

fg  任务号   例如:  fg 1

4.将后台暂停程序重新启动

我们可以使用ctrl+z让前台运行的程序暂停,bash会自动被置换回前台

bg   任务号

5.1.3 任务与进程组

有事一个任务会分配给一个进程组,而一个进程组中可能有多个进程,例如fork创建的进程,这些进程在一个组共同处理任务,所以我们也叫这些任务叫前后台任务,例如我们windows下的编译器,就是一个个进程组来共同处理任务:

看上面的vs code进程组的例子;

任务是指派给进程组的,只不过有些进程组只有一个进程罢了

5.2 实现守护进程化 

上面我们铺垫了这么久,我们究竟如何做到守护进程化呢?

我们需要分这几步:

void daemon()
{
    if(fork()!=0)exit(-1);//父进程直接退出   

    //忽略信号,让信号不会误杀进程
    signal(SIGPIPE,SIG_IGN);
    signal(SIGCLD,SIG_IGN);
    signal(SIGSTOP,SIG_IGN);

    setsid();//自成会话

    //更改pwd
    //chdir("/");

    //重定向012fd到/dev/null 堵不如疏
    int fd=open("/dev/null",O_RDWR);
    dup2(fd,0);
    dup2(fd,1);
    dup2(fd,2);

}

1.srtsid(),这个函数是用来让进程自成会话的,使得一个终端退出时,让我们的进程不会被终端的会话影响而退出,让服务器成为独立的会话中的进程,也叫自成会话;

2.让父进程退出创建子进程的原因是,我们进程组的组长不允许自成会话,所以创建出子进程,让子进程执行代码,父进程组长退出,子进程成为孤儿进程让系统领养即可

3.我们还可以修改pwd,进程的运行目录,防止被同一目录下的其他进程影响

4.最后关闭所有输出,让输出全部输出到/dev/null文件下,这个文件是专门用来提供给程序员输出不需要信息的,需要的log日志信息会通过log代码输入到我们需要的文件中;

通过这些操作,就可以让我们的进程稳定在后台运行;

其实库中提供给了我们守护线程的函数,只不过一般这个守护进程化都是我们程序员自己实现的:

第一个参数为是否修改pwd路径到"/"根目录,第二个参数为是否重定向0,1,2fd文件描述符到/dev/null文件中;

6.tcp协议过程

我们前面说了这么多函数,接下来我们来总结一下tcp协议的通信过程:

就是通过上面三次握手的过程建立了连接,其实这三次握手实际的操作,我们是看不到的都是底层的套接字在做,我们只需要调用接口就好了;四次挥手也是同理有底层套接字完成;

因为是面向连接的通信,所以服务器端会有很多个套接字和客户端连接,套接字也是通过先描述后组织的方式被管理起来的,就是说服务客户端的套接字会在服务端形成一个数据结构被管理起来;

以上便是tcp协议通信的过程;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

we3.0里的钱包是什么?

we3.0里的钱包是什么&#xff1f; 在Web3.0的语境中&#xff0c;以太坊钱包是一种专为与以太坊区块链网络及其去中心化应用&#xff08;DApps&#xff09;交互而设计的数字钱包。这种钱包不仅支持用户存储、发送和接收以太币&#xff08;ETH&#xff09;&#xff0c;还允许用户…

深入理解人工智能:从机器学习到深度学习

深入理解人工智能&#xff1a;从机器学习到深度学习 前言人工智能&#xff08;AI&#xff09;实际应用示例代码 机器学习&#xff08;ML&#xff09;分类常见算法示例代码 深度学习&#xff08;DL&#xff09;应用示例代码 神经网络&#xff08;NN&#xff09;研究方向示例代码…

使用canvas截取web camera指定区域,并生成图片

目标&#xff0c;截取红色色块背后的视频区域。 代码结构如下&#xff1a; <div id"p1"><video id"v1" autoplay playsinline></video><div id"mrz"></div><canvas id"captureCanvas"></can…

在 CentOS 安装 Python3.7 (没有弯路)

下载Python源码包 wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz安装前准备 安装依赖组件 yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel解压安装 解…

前海石公园的停车点探寻

前海石公园是真的很美&#xff0c;很多看海人&#xff0c;很多钓鱼佬&#xff0c;很多抓螃蟹的人&#xff0c;很多挖沙子的人&#xff0c;很多拍照的人&#xff0c;尤其是没有大太阳的时间段或每天傍晚或每个放假的时候人气超高&#xff0c;故前海石公园停车真的很紧张。由于前…

INTO:Web3世界的“价值引力场”

在Web3的宇宙中&#xff0c;一股强大的引力正在重塑整个数字世界的格局。这股引力&#xff0c;来自一个名为INTO的“超级连接器”。作为Web3社交领域的先锋&#xff0c;INTO正在用一种前所未有的方式重构整个产业链的价值体系。它不再满足于单一领域的创新&#xff0c;而是大胆…

MySQL之基础篇

数据库操作 1.查看当前的数据库版本 select version(); 2.显示所有数据库 show databases; 3.创建数据库 create [if not exists] database 数据库名 character set 字符编码集 collate 排序规则&#xff1b; 我们这里提前说一下 被方括号括起来的代码 表示可写可不写 示例…

1panel申请https/ssl证书自动续期

参考教程 https://hin.cool/posts/sslfor1panel.html #Acme 账户 #1panel.腾讯云dns账号 这里有一步不需要参考,腾讯云dns账号,就是子帐号授权 直接控制台搜索 访问管理 创建用户 授权搜索dns,选择第一个 点击用户名,去掉AdministratorAccess权限 5.点击api密钥生成即可…

python绘制多个wav文件的基频曲线图

任务描述 需要在一个图中绘制多个wav文件的基频图&#xff0c;具体一点&#xff0c;绘制三种不正常的基频曲线&#xff0c;和一种正常的基频曲线进行对比&#xff0c;并且将正常的基频曲线的范围画出。 代码 import pyworld as pw import librosa import numpy as npdef extr…

微调大模型(Finetuning Large Language Models)—Data_preparation(四)

1. 数据的质量 数据准备的步骤&#xff1a; 什么是tokenizing&#xff1f; 其实就是将文本数据转换为代表文本的数字&#xff0c;一般是基于字符出现的频率。需要注意的&#xff0c;编码和解码的tokenizer需保持一致&#xff0c;一般训练的模型有自己专属匹配的tokenizer。 …

实习结帖(flask加上AIGC实现设计符合OpenAPI要求的OpenAPI Schema,让AIGC运行时可以调用api,协助公司门后迁移新后端等)

终于&#xff0c;笔者的实习生活也要告一段落了&#xff0c;最后的几天都在忙着和公司做AIGC的项目&#xff0c;在搞api的设计以及公司门户网站的迁移。 牛马搬运工&#xff08;牛马了3天&#xff09; 先说这个门户网站的迁移&#xff0c;我原本以为只是换个后端&#xff08;若…

新版本Android Studio如何新建Java code工程

新版本Android Studio主推Kotlin&#xff0c;很多同学以为无法新建Java工程了&#xff0c;其实是可以的&#xff0c;如果要新建Java代码的Android工程&#xff0c;在New Project的时候需要选择Empty Views Activity&#xff0c;如图所示&#xff0c;gradle也建议选为build.grad…

RP2040 C SDK GPIO和IRQ 唤醒功能使用

RP2040 C SDK GPIO和中断功能使用 SIO介绍 手册27页&#xff1a; The Single-cycle IO block (SIO) contains several peripherals that require low-latency, deterministic access from the processors. It is accessed via each processor’s IOPORT: this is an auxiliary…

MyBatis——Plus

MyBatis——Plus怎么知道他是访问哪张表

48 旋转图像

解题思路&#xff1a; \qquad 这道题同样需要用模拟解决&#xff0c;原地算法要求空间复杂度尽量小&#xff0c;最好为 O ( 1 ) O(1) O(1)。模拟的关键是找到旋转的内在规律&#xff0c;即旋转前后的位置坐标的变化规律。 \qquad 正方形矩阵类似洋葱&#xff0c;可以由不同大小…

计算机毕业设计 在线问诊系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

又一条地铁无人线开通!霞智科技智能清洁机器人正式“上岗”

2024年9月26日12时&#xff0c;又一条无人线开通运营&#xff0c;这是陕西省首条全自动无人驾驶地铁线路。该线路作为北跨战略的先行工程&#xff0c;是连接主城区与渭北地区的轨道交通快线&#xff0c;对优化城市总体空间布局、推动区域融合发展、促进沿线产业升级具有十分重要…

电脑上数据丢了怎么找回来 Win系统误删文件如何恢复

无论是在工作中&#xff0c;还是生活中&#xff0c;电脑都是不可缺少的重要工具&#xff0c;尤其是在工作中&#xff0c;电脑不仅可以高效的完成工作&#xff0c;还可以存储工作中的重要资料。不过在使用电脑的时候&#xff0c;也会遇到数据丢失的情况。针对这一问题&#xff0…

渗透测试--文件上传常用绕过方式

文件上传常用绕过方式 1.前端代码&#xff0c;限制只允许上传图片。修改png为php即可绕过前端校验。 2.后端校验Content-Type 校验文件格式 前端修改&#xff0c;抓取上传数据包&#xff0c;并且修改 Content-Type 3.服务端检测&#xff08;目录路径检测&#xff09; 对目…

在Java中使用GeoTools解析POI数据并存储到PostGIS实战

目录 前言 一、POI数据相关介绍 1、原始数据说明 2、空间数据库表设计 二、POI数据存储的设计与实现 1、对应的数据模型对象的设计 2、属性表数据和空间信息的读取 3、实际运行结果 三、总结 前言 POI点&#xff0c;全称为Point of Interest&#xff08;兴趣点&#xf…