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

网站备案是否关闭如何更好的营销

网站备案是否关闭,如何更好的营销,大学生创新创业项目计划书,鄂州最新通告今天🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程 这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目…
🔥 个人主页:大耳朵土土垚
🔥 所属专栏:Linux系统编程

这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉

文章目录

  • 1. TCP socket API 详解
    • socket()
    • bind()
    • listen()
    • accept()
    • connect()
  • 2. Echo Server
    • TCP服务器
      • 多进程版本
      • 多线程版本
      • 线程池版本
    • TCP客户端

1. TCP socket API 详解

  下面介绍程序中用到的 socket API,这些函数都在 sys/socket.h 中。

socket()

在这里插入图片描述

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

bind()

在这里插入图片描述

  • 介绍:服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用 bind 绑定一个固定的网络地址和端口号;

  • 返回值: bind()成功返回 0,失败返回-1。

  • 作用:将参数 sockfdmyaddr 绑定在一起, 使 sockfd 这个用于网络通讯的文件描述符监听 myaddr 所描述的地址和端口号;

  • 参数: 前面讲过,struct sockaddr *是一个通用指针类型,myaddr 参数实际上可以接受多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需要第三个参数 addrlen指定结构体的长度;我们的程序中对 myaddr 参数是这样初始化的:
    在这里插入图片描述
      1. 将整个结构体清零;
      2. 设置地址类型为 AF_INET;
      3. 网络地址为 INADDR_ANY, 这个宏表示本地的任意 IP 地址,因为服务器可能有
    多个网卡,每个网卡也可能绑定多个 IP 地址, 这样设置可以在所有的 IP 地址上监听, 直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址;
      4. 端口号为 SERV_PORT, 我们定义为 8080;

listen()

在这里插入图片描述

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

accept()

在这里插入图片描述

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

我们的服务器程序结构是这样的:
在这里插入图片描述

  • 返回值:sockfd用来进行通信

connect()

在这里插入图片描述

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

2. Echo Server

  有了上面的接口,我们就可以实现以TCP为基础的简单消息回显服务器了,运行结果应该如下图所示:

在这里插入图片描述
代码如下:

TCP服务器

#pragma once#include <iostream>
#include <string.h>
#include <cstdlib>
#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 "InetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"#define BACKLOG 8
using namespace InetAddrModule;
using namespace LogModule;
static const uint16_t defaultport = 8888;class TcpServer
{
public:TcpServer(uint16_t port = defaultport) : _port(port), _listensockfd(-1), _isruning(false){}void InitServer(){// 1.创建Tcp套接字_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::ERROR) << "InitServer socket fail ...";Die(SOCKET_ERR);}// 填充信息struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = ::htons(_port);//aaa注意要转网络!!!!!!!!!!serveraddr.sin_addr.s_addr = INADDR_ANY; // 表示可以接收任意地址的信息// 2. bind;int n = ::bind(_listensockfd, CONV(&serveraddr), sizeof(serveraddr));if (n < 0){LOG(LogLevel::ERROR) << "InitServer bind fail ...";Die(BIND_ERR);}// 3.监听int m = ::listen(_listensockfd, BACKLOG);if (m < 0){LOG(LogLevel::ERROR) << "InitServer listen fail ...";Die(LISTEN_ERR);}LOG(LogLevel::INFO) << "ServerInit success...";}void handler(int sockfd){char buffer[4096];while (true){ssize_t n = ::read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;LOG(LogLevel::INFO) << buffer;std::string echo_string = "server echo# ";echo_string += buffer;::write(sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // client 退出{LOG(LogLevel::INFO) << "client quit: " << sockfd;break;}else{// 读取失败break;}}::close(sockfd); // fd泄漏问题!}void Start(){_isruning = true;while(_isruning){struct sockaddr_in peer;socklen_t peerlen = sizeof(peer); // 这个地方一定要注意,要不然,会有问题!LOG(LogLevel::DEBUG) << "accepting ...";// 我们要获取client的信息:数据(sockfd)+client socket信息(accept || recvfrom)int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0){LOG(LogLevel::ERROR) << "StartServer accept fail ...";continue; // 继续接收}LOG(LogLevel::INFO)<<"ServerStart success...";// 连接成功后就可以通信handler(sockfd);}_isruning = false;}~TcpServer(){}private:uint16_t _port;int _listensockfd;bool _isruning;
};               

与Udp服务器不同的是,Tcp服务要求我们先调用listen接口监听,然后在通过accept和客户端使用connet建立连接后才可以进行通信;所以如果仅仅使用单进程是无法满足同时接收多个客户端的消息,下面将会给出多进程、多线程以及基于线程池实现的Tcp服务。

多进程版本

//其他的不变
void Start(){_isruning = true;while(_isruning){struct sockaddr_in peer;socklen_t peerlen = sizeof(peer); // 这个地方一定要注意,要不然,会有问题!LOG(LogLevel::DEBUG) << "accepting ...";// 我们要获取client的信息:数据(sockfd)+client socket信息(accept || recvfrom)int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0){LOG(LogLevel::ERROR) << "StartServer accept fail ...";continue; // 继续接收}LOG(LogLevel::INFO)<<"ServerStart success...";// 连接成功后就可以通信//version1: 多进程pid_t id = ::fork();if(id == 0)//子进程{::close(_listensockfd);//要关掉不需要的文件描述符,避免fd泄露问题if(fork())//子进程再创建孙子进程::exit(0);//让子进程退出,孙子进程成为孤儿进程,这样就不用父进程回收//孙子进程处理,结束后由操作系统回收handler(sockfd);::exit(0);}::close(sockfd);//子进程退出后,父进程就不会阻塞在这里,继续接收其他客户端连接int rid = ::waitpid(id, nullptr, 0);if(rid < 0)LOG(LogLevel::WARNING) << "ServerStart waitpid error...";}_isruning = false;}

对于多进程,首先每个进程都有自己的文件描述符表,所以父子进程都需要关闭自己不需要的文件描述符;

其次父进程需要等待回收子进程,此时父进程会阻塞直到子进程完成通信,这样和之前单进程通信效果一样,所以为了不让父进程阻塞,子进程需要再创建子进程,用它来完成通信,此时父进程就可以直接回收子进程,孙子进程就成为孤儿进程进行通信,结束后由操作系统回收。

多线程版本

    struct ThreadData{int sockfd;TcpServer *self;};static void *ThreadEntry(void *args){pthread_detach(pthread_self()); // 线程分离,线程执行结束后自动被系统回收ThreadData *data = (ThreadData *)args;data->self->handler(data->sockfd);return nullptr;}void Start(){_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t peerlen = sizeof(peer); // 这个地方一定要注意,要不然,会有问题!LOG(LogLevel::DEBUG) << "accepting ...";// 我们要获取client的信息:数据(sockfd)+client socket信息(accept || recvfrom)int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0){LOG(LogLevel::ERROR) << "StartServer accept fail ...";continue; // 继续接收}LOG(LogLevel::INFO) << "ServerStart success...";// 连接成功后就可以通信// version2: 多线程// 主线程和新线程共享一张文件描述符表pthread_t tid;ThreadData *data = new ThreadData;data->sockfd = sockfd;data->self = this;pthread_create(&tid, nullptr, ThreadEntry, data);
}_isruning = false;}

设置线程分离这样线程执行完毕后就可以自动被系统回收

线程池版本

using task_t = std::function<void()>;void Start(){_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t peerlen = sizeof(peer); // 这个地方一定要注意,要不然,会有问题!LOG(LogLevel::DEBUG) << "accepting ...";// 我们要获取client的信息:数据(sockfd)+client socket信息(accept || recvfrom)int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0){LOG(LogLevel::ERROR) << "StartServer accept fail ...";continue; // 继续接收}LOG(LogLevel::INFO) << "ServerStart success...";// version-3:线程池版本 比较适合处理短任务,或者是用户量少的情况ThreadPool<task_t>::GetInstance()->Enqueue([this, sockfd](){ this->handler(sockfd); });}_isruning = false;}

引入之前实现的线程池,并使用单例模式

使用服务器代码如下:

#include "TcpServer.hpp"int main()
{std::unique_ptr<TcpServer> tcpserver = std::make_unique<TcpServer>();tcpserver->InitServer();tcpserver->Start();return 0;
}

TCP客户端

#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"using namespace LogModule;
using namespace InetAddrModule;int sockfd = -1;//./udp_client server_ip server_port
int main(int argc, char *argv[])
{if (argc != 3){LOG(LogLevel::ERROR) << "Usage:" << argv[0] << " serverip serverport";Die(ARGV_ERR);}// 1.创建sockfdsockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){LOG(LogLevel::WARNING) << "client sockfd fail...";Die(SOCKET_ERR);}// 2.填充服务器信息std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// InetAddr serveraddr(serverip, serverport); struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(serverport);serveraddr.sin_addr.s_addr = inet_addr(serverip.c_str());// 3.与服务器建立连接int n = ::connect(sockfd, CONV(&serveraddr), sizeof(serveraddr));if (n < 0){LOG(LogLevel::ERROR) << "ClientConnet fail...";Die(CONNET_ERR);}// 4. 发送请求给服务器while (true){// 4.1获取信息std::cout << "Please Enter# ";std::string message;std::getline(std::cin, message);// 4.2发送信息给服务器ssize_t n = ::sendto(sockfd, message.c_str(), sizeof(message), 0, CONV(&serveraddr), sizeof(serveraddr));if (n < 0){LOG(LogLevel::ERROR) << "client sendto fail...";continue;}// 4.3从服务器接收信息char buffer[1024];struct sockaddr_in tmp;socklen_t len = sizeof(tmp);ssize_t m = ::recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, CONV(&tmp), &len);if (m > 0){buffer[m] = 0;std::cout << buffer << std::endl;}else{LOG(LogLevel::ERROR) << "client recvfrom fail...";}}::close(sockfd);return 0;
}

与UDP客户端相比,TCP客户端需要与服务器通过connet连接后才能通信。

http://www.yayakq.cn/news/225544/

相关文章:

  • 深圳网站建设费用大概南宁百度快速排名优化
  • html5网站开发方案房产信息官网查询
  • wordpress建站 ftpseo网站外链平台
  • 广州市手机网站建设品牌容桂网站制作公司
  • 个人可做网站需要什么材料电子商务网站建设好么
  • 怎样查看别人的网站是怎么建设上海知名公司
  • 西安网站建设建站系统windows版本的wordpress
  • 艺缘网站的建设基础微网站开发代理商
  • 黄骅市住房和城乡建设局网站h5企业网站模板 下载
  • 巴零网站建设个人网站备案麻烦吗
  • 哈尔滨网站运营服务商用excel做网站
  • 免费ppt资源网站卓商网站建设公司
  • 中华建设网算什么级别网站wordpress 制作widget
  • 苏州做物流网站电话电商无货源怎么做
  • 吴桥县做网站衡阳网站设计ss0734
  • 网站项目开发的流程遵义花果园网站建设
  • 网站建设是怎么挣钱无锡网站设计厂家
  • 保定手机网站小程序制作价格北京华网天下实惠
  • 有什么好看的网站资源设计展厅的公司
  • 怎样做网站轮播网站一般怎么维护
  • 宠物商品销售网站开发背景网站建设合同以及服务条款
  • 怎样帮人做网站挣钱定制开发软件开发
  • WordPress建站布置知名设计公司logo
  • 网站建设要会什么软件图片制作在线制作免费
  • 网站建设开票项目是什么怎么网络推广自己业务
  • 广州市营销型网站建设可以做logo设计单子的网站
  • 智能自助建站系统源码怎么制作php网站
  • 怎么查询网站的域名哪个行业最喜欢做网站
  • 网站标题怎么隔开开发门户网站报价
  • 海口智能建站价格wordpress wp-postviews