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;// 后bindstruct sockaddr_in local;// 网络通信用sockaddr_in, 本地用sockaddr_unmemset(&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;// 监听socketuint16_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底层自动bindint 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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【运维心得】Centos7安装Redis7.4.2并处理相关告警

概述 单机版的redis安装比较简单&#xff0c;这里重点记录下告警的处理。 安装步骤 1. 确认版本 可以通过官方仓库或者知名的网站获取最新安装包&#xff0c;截止20250213&#xff0c;未找到官方安装包。 rpmfind: RPM resource redis(x86-64)https://rpmfind.net/linux/rpm2h…

社区版IDEA中配置TomCat(详细版)

文章目录 1、下载Smart TomCat2、配置TomCat3、运行代码 1、下载Smart TomCat 由于小编的是社区版&#xff0c;没有自带的tomcat server&#xff0c;所以在设置的插件里面搜索&#xff0c;安装第一个&#xff08;注意&#xff1a;安装时一定要关闭外网&#xff0c;小编因为这个…

K8s之存储卷

一、容忍、crodon和drain 1.容忍 即使节点上有污点&#xff0c;依然可以部署pod。 字段&#xff1a;tolerations 实例 当node01上有标签test11&#xff0c;污点类型为NoSchedule&#xff0c;而node02没有标签和污点&#xff0c;此时pod可以在node01 node02上都部署&#xff0c…

【MySQL — 数据库基础】深入解析 MySQL 的联合查询

1. 插入查询结果 语法 insert into table_name1 select* from table_name2 where restrictions ;注意&#xff1a;查询的结果集合&#xff0c;列数 / 类型 / 顺序 要和 insert into 后面的表相匹配&#xff1b;列的名字不要求相同&#xff1b; create table student1(id int , …

spring cloud 使用 webSocket

1.引入依赖,(在微服务模块中) <!-- Spring WebSocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 2.新建文件 package com.ruoyi.founda…

《aarch64汇编从入门到精通》-204页PPT+实验

&#x1f534;【课程特色】 ✅1、依照官方文档总结制作&#xff0c;体系更完整&#xff0c;不遗漏知识&#xff1b; ✅2、基于Armv8/Armv9架构讲解汇编。真正的ARM汇编&#xff1b; ✅3、资料更全。200多页PPT资料&#xff0c;其它参考资料; ✅4、学完ARM架构&#xff0c;再学汇…

企业使用统一终端管理(UEM)工具提高端点安全性

什么是统一终端管理(UEM) 统一终端管理(UEM)是一种从单个控制台管理和保护企业中所有端点的方法&#xff0c;包括智能手机、平板电脑、笔记本电脑、台式机和 IoT设备。UEM 解决方案为 IT 管理员提供了一个集中式平台&#xff0c;用于跨所有作系统和设备类型部署、配置、管理和…

20250213 隨筆 雪花算法

雪花算法&#xff08;Snowflake Algorithm&#xff09; 雪花算法&#xff08;Snowflake&#xff09; 是 Twitter 在 2010 年開發的一種 分布式唯一 ID 生成算法&#xff0c;它可以在 高併發場景下快速生成全局唯一的 64-bit 長整型 ID&#xff0c;且不依賴資料庫&#xff0c;具…

QT 异步编程之多线程

一、概述 1、在进行桌面应用程序开发的时候&#xff0c;假设应用程序在某些情况下需要处理比较复制的逻辑&#xff0c;如果只有一个线程去处理&#xff0c;就会导致窗口卡顿&#xff0c;无法处理用户的相关操作。这种情况下就需要使用多线程&#xff0c;其中一个线程处理窗口事…

leetcode 543. 二叉树的直径

题目如下 数据范围 示例 显然直径等于左右子树高之和的最大值。通过代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* Tr…

IP 路由基础 | 路由条目生成 / 路由表内信息获取

注&#xff1a;本文为 “IP 路由” 相关文章合辑。 未整理去重。 IP 路由基础 秦同学学学已于 2022-04-09 18:44:20 修改 一. IP 路由产生背景 我们都知道 IP 地址可以标识网络中的一个节点&#xff0c;并且每个 IP 地址都有自己的网段&#xff0c;各个网段并不相同&#xf…

sql:时间盲注和boolen盲注

关于时间盲注&#xff0c;boolen盲注的后面几个获取表、列、具体数据的函数补全 时间盲注方法 import time import requests# 获取数据库名 def inject_database(url):dataname for i in range(1, 20):low 32high 128mid (low high) // 2while low < high:payload &q…

zyNo.22

常见Web漏洞解析 命令执行漏洞 1.Bash与CMD常用命令 &#xff08;1&#xff09;Bash 读取文件&#xff1a;最常见的命令cat flag 在 Bash 中&#xff0c;cat 以及的tac、nl、more、head、less、tail、od、pr 均为文件读取相关命令&#xff0c;它们的区别如下&#xff1a; …

《Python 中 JSON 的魔法秘籍:从入门到精通的进阶指南》

在当今数字化时代&#xff0c;网络编程无处不在&#xff0c;数据的高效传输与交互是其核心。JSON 作为一种轻量级的数据交换格式&#xff0c;凭借其简洁、易读、跨语言的特性&#xff0c;成为网络编程中数据传输与存储的关键技术。无论是前后端数据交互&#xff0c;还是不同系统…

部门管理(体验,最原始方法来做,Django+mysql)

本人初学&#xff0c;写完代码在此记录和复盘 在创建和注册完APP之后&#xff08;我的命名是employees&#xff09;&#xff0c;编写models.py文件创建表 手动插入了几条数据 1.部门查询 urls.py和views.py在编写之前&#xff0c;都要注意导入对应的库 urls.py&#xff1a;…

Docker的容器

Docker的容器 一&#xff0e;容器 容器是一种轻量级的虚拟化技术。它有效的将单个操作系统的资源划分到各独立的组中&#xff0c;以便更好的平衡这些独立的组之间资源的使用。 容器主要包含了命名空间&#xff08;Namespaces&#xff09;和cgroup&#xff08;Control Groups…

[Redis] Redis分布式锁与常见面试题

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Word 公式转 CSDN 插件 发布

经过几个月的苦修&#xff0c;这款插件终于面世了。 从Word复制公式到CSDN粘贴&#xff0c;总是出现公式中的文字被单独提出来&#xff0c;而公式作为一个图片被粘贴的情况。公式多了的时候还会导致CSDN禁止进一步的上传公式。 经过对CSDN公式的研究&#xff0c;发现在粘贴公…

JVM——堆的回收:引用计数发和可达性分析法、五种对象引用

目录 引用计数法和可达性分析法 引用计数法&#xff1a; 可达性分析算法&#xff1a; 五种对象引用 软引用&#xff1a; 弱引用&#xff1a; 引用计数法和可达性分析法 引用计数法&#xff1a; 引用计数法会为每个对象维护一个引用计数器&#xff0c;当对象被引用时加1&…

sqlilabs--小实验

一、先盲注判断 ?id1 and sleep(2)-- 如果发现页面存在注点&#xff0c;使用时间盲注脚本进行注入 import requestsdef inject_database(url):name for i in range(1, 20): # 假设数据库名称长度不超过20low 48 # 0high 122 # zmiddle (low high) // 2while low &l…