当前位置: 首页 > news >正文

【Linux网络】TCP_Socket

目录

TCP协议(传输控制协议)

listen状态

accept和connect

TCP_echo_server

(1)创建套接字

(2)绑定

(3)设置listen状态

(4)loop

(5)客户端

多线程远程执行命令


TCP协议(传输控制协议)

传输层协议
有连接
可靠传输(前提网络要联通,复杂,维护性要更强)
面向字节流

tcp和udp在创建套接字和绑定部分是一样的;

不同的是,tcp在服务端绑定后需要将套接字的状态设置为listen状态,除此之外,在通信前要获取新连接;在客户端也要connect;

因为tcp是面向连接的,tcp需要未来不断地能够做到获取连接;

listen状态

accept和connect


  

 


 在TCP(传输控制协议)的服务端中获取新连接是至关重要的,原因如下:

  •  建立连接

TCP是面向连接的协议。客户端发起连接请求(通过 connect 函数),服务端需要接受这些请求(通过 accept 函数)来建立连接。这个连接是双向的通信链路,允许数据在客户端和服务端之间可靠地传输。例如,当你访问一个网站时,你的浏览器(客户端)向Web服务器(服务端)发起TCP连接请求,服务器接受这个请求后,两者之间就建立了一条可靠的通信链路,用于传输网页数据。

  •  并发处理

服务端通常需要同时处理多个客户端的请求。通过获取新连接,服务端可以为每个客户端创建一个独立的连接,从而实现并发处理。例如,一个在线游戏服务器可能需要同时处理成千上万的玩家连接。每个玩家的客户端都会向服务器发起TCP连接请求,服务器接受这些请求并创建对应的连接,这样就能同时为多个玩家提供服务。

  • 数据交互

获取新连接是数据传输的起点。一旦连接建立,客户端和服务端就可以通过读写套接字( read 和 write 函数,或者 recv 和 send 函数)来交换数据。例如,在一个文件传输服务中,客户端请求下载文件,服务端接受连接后,就可以通过已建立的连接将文件数据发送给客户端。
 

TCP_echo_server

下面我们来手动实现一下tcp_echo_server;按照下面的流程来实现:

(1)创建套接字

// 1、创建套接字
_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (_listensockfd < 0)
{LOG(FATAL, "socket error\n");exit(SOCKET_ERROR);
}
LOG(DEBUG, "socket create sucess\n");

(2)绑定

struct sockaddr_in local;
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, (struct sockaddr *)&local, sizeof(local));

(3)设置listen状态

// 3、设置套接字为listen状态
// 因为tcp是面向连接的,tcp需要未来不断地能够做到获取连接
int m = listen(_listensockfd, MAXCOUNT);
if (m < 0)
{LOG(FATAL, "listen error\n");
}
LOG(INFO, "listen sucess\n");

(4)loop

(1)获取新连接

// 获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{LOG(WARNING, "accept error\n");continue;
}
InetAddr addr(client);
LOG(INFO, "get a new link,client info :%s\n", addr.Addrstr().c_str());

(2)

因为下面的几个版本都需要用的Service函数,我先提前放到这里;

    void Service(int sockfd, InetAddr addr){while (true){char inbuffer[2048];ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1); // 读消息,-1是因为它是系统调用不会做字符串处理if (n > 0){inbuffer[n] = 0;string echo_string = "[server echo]# ";echo_string += inbuffer;n = ::write(sockfd, echo_string.c_str(), echo_string.size()); // 写消息}else if (n == 0) // 读到文件结尾,也就是通信结束{LOG(INFO, "client %s quit\n", addr.Addrstr().c_str());break;}else{LOG(ERROR, "read server error:%s\n", addr.Addrstr().c_str());break;}}}

1、version 0 版本

Service(sockfd, addr);

2、version 1 ----多进程版本

 pid_t id =fork();
if(id==0)
{
::close(_listensockfd);
if(fork()>0)exit(0);//子进程退出
Service(sockfd, addr);//孙进程会造成僵尸进程
exit(0);
}
//father
::close(sockfd);
int n=waitpid(id,nullptr,0);//1、忽略;2、父子孙
if(n>0)
{
LOG(INFO,"wait child sucess\n");
}

3、version 2 ----多线程版本

pthread_t tid;
ThreadDate *td = new ThreadDate(sockfd, this, addr);
pthread_create(&tid, nullptr, Execute, td); // 新线程分离

4、version 3 ---线程池版本

task_t aa=std::bind(&TcpServer::Service,this,sockfd,addr);
ThreadPool<task_t>::GetInstance()->Equeue(aa);

(5)客户端

以上我们就完成了tcp_echo_server,也就是用tcp实现的一个简单的通信;

多线程远程执行命令

这里我们用version 2 多线程版本来实现远程执行命令;

为了使代码更加美观,我们在version2版本的基础上,将Service函数进行封装;将它封装到command.hpp的HandlerCommand函数里面;

这样我们调用回调函数,然后再主函数中进行绑定即可;

using command_server_t =std::function<void(int sockfd, InetAddr addr)>;
void HandlerCommand(int sockfd, InetAddr addr)
{while (true){char inbuffer[2048];ssize_t n = ::recv(sockfd, inbuffer, sizeof(inbuffer) - 1,0); // 读消息,-1是因为它是系统调用不会做字符串处理             if (n > 0){inbuffer[n] = 0;string result = Excute(inbuffer);n = ::send(sockfd, result.c_str(), result.size(),0); // 写消息}else if (n == 0) // 读到文件结尾,也就是通信结束{LOG(INFO, "client %s quit\n", addr.Addrstr().c_str());break;}else{LOG(ERROR, "read server error:%s\n", addr.Addrstr().c_str());break;}}
}

完成上面的准备工作,怎么执行命令?

在之前我写过手写shell的文章,可以直接用里面的代码,文章用的是exec*函数来执行命令;但是这里我们用一个更加简单的函数:popen直接执行即可;

string Excute(const string &cmdstr)
{FILE *fp=popen(cmdstr.c_str(),"r");string result;if(fp){char line[1024];while(fgets(line,sizeof(line),fp))//读文件{result+=line;}return result;}else{return "execute error";}
}

完整代码:

tcp_echo_server:

登录 - Gitee.com

远程执行命令:

登录 - Gitee.com

http://www.lryc.cn/news/473474.html

相关文章:

  • NVR批量管理软件/平台EasyNVR多个NVR同时管理支持视频投放在电视墙上
  • Springboot集成阿里云通义千问(灵积模型)
  • 微信公众号(或微信浏览器)获取openId(网页授权)
  • C++算法第五天
  • 牛客网剑指Offer-树篇-JZ26 树的子结构
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发六,使用SDLVSQT显示yuv文件
  • Spring 设计模式之适配器模式
  • 多传感器数字化分析系统
  • Java 基础教学:面向对象编程基础-封装、继承与多态
  • Ubuntu环境本地部署DbGate数据库管理工具并实现无公网IP远程访问
  • 【AI抠图整合包及教程】Meta SAM 2:视觉分割的革命性飞跃
  • 使用语言模型进行文本摘要的五个级别(llm)
  • ubuntu交叉编译libffi库给arm平台使用
  • 【jvm】空间分配担保策略
  • iQOO手机怎样将屏幕投射到MacBook?可以同步音频吗?
  • BUU usualCrypt1
  • 第十七章 标准库特殊设施
  • 【格言分享】程序员的经典名言解读
  • SpringBoot接收LocalDateTime参数
  • Typora配置GitHub图床--结合PicGo
  • 【书生.浦语实战营】——入门岛
  • WPF+MVVM案例实战(十四)- 封装一个自定义消息弹窗控件(下)
  • 嵌入式——STM32外设应用
  • HCIA(ACL)
  • react基础之reactHooks
  • Java基础0-Java概览
  • SW绘制曲面
  • css知识点梳理2
  • 攻防世界 MISC miao~详解
  • 使用 `tracert [options] <目标地址>` 命令的详细介绍