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

Linux_Socket_TCP

18fde01fee5e4278981004762ce48cc4.png

✨✨ 欢迎大家来到小伞的大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:LInux
小伞的主页:xiaosan_blog

gitee:许星让 (xu-xingrang) - Gitee.com

制作不易!点个赞吧!!谢谢喵!!

0.本节目录gitee链接

完整代码:

lesson40 · 许星让/linux gcc - 码云 - 开源中国

1.TCP socket API 详解

如图:客户端与服务端通过类似于文件描述符的进行读写

  • socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
  • 应用程序可以像读写文件一样用read/write在网络上收发数据;
  • 如果 socket()调用出错则返回-1
  • 对于 IPv4,family 参数指定为 AF_INET;(即将接收地址设置为0,任何连接客户端,都接收)
  • 对于 TCP 协议,type 参数指定为 SOCK_STREAM,表示面向流的传输协议
  • protocol参数的介绍从略,指定为0即可

1.2 bind 

  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接;服务器需要调用bind绑定一个固定的网络地址和端口号;
  • bind()成功返回 0,失败返回-1。
  • bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号;
  • 前面讲过,structsockaddr*是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度;

我们的程序中对myaddr参数是这样初始化的:

  1. 将整个结构体清零;
  2. 设置地址类型为AF_INET;
  3. 网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址,这样设置可以在所有的 IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
  4. 端口号为SERV_PORT,我们定义为9999;

1.4 listen

  • listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,这里设置不会太大(一般是5)
  • listen()成功返回 0,失败返回-1;

1.5 accept

  • 三次握手完成后,服务器调用accept()接受连接
  • 如果服务器调用 accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;
  • addr是一个传出参数,accept()返回时传出客户端的地址和端口号;
  • 如果给 addr 参数传 NULL,表示不关心客户端的地址;
  • addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的,缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);

1.6 connect

  • 客户端需要调用connect()连接服务器;
  • connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址,而connect 的参数是对方的地址;
  • connect()成功返回0,出错返回-1;

2. Tcpserver

2.1 TcpServer.cc

#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include <sys/wait.h>
#include <signal.h>
#include "ThreadPool.hpp"// 服务器往往是禁止拷贝的
using namespace LogModule;
using namespace ThreadPoolModule;// using task_t = std::function<void()>;
using func_t = std::function<std::string(const std::string &, InetAddr &word)>;const static int defaultsockfd = -1;
const static int backlog = 10;class TcpServer : public Nocopy
{
public:TcpServer(uint16_t port, func_t func): _port(port),_func(func),_listensockfd(defaultsockfd),_isrunning(false){}void Init(){signal(SIGCHLD, SIG_IGN); // 子进程退出发送SIGCHLD信号,用SIG_IGN信号忽略子进程信号// 1. 创建套接字文件_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error!";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd;// 2. bind 绑定端口号InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd;// 设置_sockfd为监听状态n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd;}void Service(int sockfd, InetAddr &peer){char buffer[1024];while (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " say#" << buffer;std::string echo_string = _func(buffer, peer);// std::string echo_string = "echo# ";// echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}class ThreadData{public:ThreadData(int sockfd, InetAddr addr, TcpServer *tsvr): _sockfd(sockfd),_addr(addr),_tsvr(tsvr){}public:int _sockfd;InetAddr _addr;TcpServer *_tsvr;};static void *Routine(void *args){pthread_detach(pthread_self()); // 分离线程ThreadData *td = static_cast<ThreadData *>(args);td->_tsvr->Service(td->_sockfd, td->_addr);delete td;return nullptr;}void Run(){_isrunning = true;while (_isrunning){// a.获取状态struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// sockfd 提供服务,_listensockfd 提供链接int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){LOG(LogLevel::WARNING) << "accept error";continue;}InetAddr addr(peer);LOG(LogLevel::INFO) << "accept sucess, peer addr : " << addr.StringAddr();// 多线程适合长服务ThreadData *td = new ThreadData(sockfd, addr, this);pthread_t tid;pthread_create(&tid, nullptr, Routine, td);// 0.单进程版本// Service(sockfd, addr);// 1.多进程版本// pid_t id = fork();// if (id < 0)// {//     LOG(LogLevel::FATAL) << "fork error";//     exit(FORK_ERR);// }// else if (id == 0)// {//     // 子进程//     if (fork() > 0)//         exit(OK);//     //孙子进程//     //当子进程返回,孙子进程为孤儿进程,父进程变为系统bash,系统自动回收进程//     close(_listensockfd); // 关闭不相关的管道//     Service(sockfd, addr);//     exit(OK);// }// else// {//     // 父进程//     close(sockfd);//     pid_t rid = waitpid(id, nullptr, 0); // 阻塞等待// }// 2.多线程// ThreadData *td = new ThreadData(sockfd, addr, this);// pthread_t tid;// pthread_create(&tid, nullptr, Routine, td);// //pthread_join()线程等待,也会导致线程串行,使用pthread_detach线程分离,无需等待;// version3:线程池版本,线程池一般比较适合处理短服务// 将新链接和客户端构建一个新的任务,push线程池中// ThreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,&addr](){//     this->Service(sockfd,addr);// });}_isrunning = false;}~TcpServer() {}private:int _port;int _listensockfd; // 监听socketbool _isrunning;func_t _func; // 设置回调处理
};

2.2 TcpServer.hpp

#include "TcpServer.hpp"
#include "Dict.hpp"
#include "Command.hpp"
void Usage(std::string proc)
{std::cerr << "Usage" << proc << " port" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);Enable_Console_Log_Strategy();// Dict d;// d.LoadDict();Command cmd;//命令行xshellfunc_t ret = std::bind(&Command::Execute,&cmd,std::placeholders::_1,std::placeholders::_2);std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port,ret);// std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port, [&cmd](const std::string &command, InetAddr &addr)// { return cmd.Execute(command, addr); });//翻译// std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port);// std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port, [&d](const std::string &word, InetAddr &addr)// { return d.Translate(word, addr); });tsvr->Init();tsvr->Run();return 0;
}
http://www.dtcms.com/a/569897.html

相关文章:

  • 拼多多福利券小程序怎么赚钱潍坊seo管理
  • JAVA国际版同城外卖跑腿团购到店跑腿多合一APP系统源码支持Android+IOS+H5
  • 做电锯电音的网站古董手表网站
  • 电力工程设计AI推荐:良策金宝AI以“六大智能”重塑行业效率
  • Yolo12改进策略:下采样改进|IPFA,下采样|信息保留特征聚合模块|即插即用
  • 网站seo内部优化怎么推广平台
  • 零陵区住房和城乡建设局网站百度网址域名大全
  • 0基础学舞蹈,学习计划
  • Redis_4_常见命令(完)+认识数据类型和编码方式
  • 代码交易网站邯郸网站建设费用
  • 黑色网站源码三河市网站建设
  • 20251104让AIO-3576Q38开发板跑Rockchip的原厂Android14之后适配GPIO扩展芯片PCA9555
  • Python基于PyTorch实现多输入多输出进行LSTM循环神经网络回归预测项目实战
  • Hadess零基础学习,如何管理Helm制品
  • 今日行情明日机会——20251104
  • 校园网站建设多少钱网站的公告轮播效果怎么做
  • 网站演示程序上海广告公司招聘信息
  • 中小企业等保合规成本控制:上海云盾低成本安全建设方案
  • MATLAB实现灰度图像二维傅里叶变换
  • Photoshop通道中的基本操作
  • YOLOv5(PyTorch)目标检测实战:TensorRT加速部署!训练自己的数据集(Ubuntu)——(人工智能、深度学习、机器学习、神经网络)
  • 网站推广与优化怎么做大型平面设计网站
  • STM32H743-ARM例程37-NETIO
  • golang 网站开发 教程自己做网站代理产品
  • 构建1688店铺商品数据集:Python爬虫数据采集与格式化实践
  • JavaEE初阶——多线程(5)单例模式和阻塞队列
  • Dart | 安装基础环境和快速入门(保姆级教程)
  • 网站购买域名八年级信息网站怎么做
  • 海阳建设局网站php开源公司网站
  • 山东手机网站建设标致品牌设计公司