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

如何做网站 百度网络推广网站

如何做网站 百度,网络推广网站,嵌入式软件开发公司哪家好,网站建设前置审批目录 引入 基础版 服务端 思路 头文件log类 套接字的初始化 思路 代码 服务器开始运行 思路 代码 注意点 -- ip地址和端口号的来源 ip地址的选择 本地环回地址 端口号 ​编辑 运行情况 netstat -nlup 客户端 思路 初始化 思路 代码 客户端的运行 思…

目录

引入

基础版

服务端

思路

头文件+log类

套接字的初始化

思路

代码 

服务器开始运行

思路

代码

注意点 -- ip地址和端口号的来源

ip地址的选择

本地环回地址

端口号

​编辑

运行情况

netstat -nlup

客户端

思路

初始化

思路

代码 

客户端的运行

思路 

代码

运行情况 

运行情况 

改成命令行输入

服务端

客户端

运行情况

远程让服务器执行命令

引入

分层

代码

运行情况 


引入

我们将使用udp协议+ipv4协议来写cs之间的网络通信

  • 将服务端运行在我们的云服务器上,其他机器可以运行客户端来向服务端发送消息
  • 服务端收到消息后,经过一定处理直接发回给相应的客户端
  • 并且,我们将一步一步改善我们的代码,让他变得更加美观+拥有更多功能

基础版

服务端将收到的数据经过包装后直接回传给发送端,类似于echo

服务端

思路

封装成一个类,提供运行接口即可

启动后创建套接字,并保持等待数据的状态

头文件+log类

下面是用到的头文件:

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <cstring>#include <string>
#include <iostream>#include "Log.hpp"

以及用来提示消息的log类

数据格式 -- 时间+消息

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define INFO 0
#define DEBUG 1
#define WARNING 2
#define ERROR 3
#define FATAL 4 // 致命的错误#define SIZE 1024class Log
{
public:Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);printf("%s\n", logtxt);}~Log(){}private:std::string levelToString(int level){switch (level){case INFO:return "INFO";case DEBUG:return "DEBUG";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "NONE";}}
};

套接字的初始化

思路

首先创建出套接字文件

  • 这里使用udp协议+ipv4网络协议
  • 所以,可以确定socket函数中使用的参数为AF_INET, SOCK_DGRAM

然后我们需要构建一个存放了网络地址信息(ip+端口号+其他信息)的结构体

  • 传参传的是公共类型的结构体,但实际使用的还是那两个中的一个
  • 所以需要手动构建一个
  • 初始化可以用memset,也可以用bzero
  • 端口号需要在网络中传输,但它最初是在类内初始化的,是跟随系统字节序列来的

    我们无法保证系统一定是大端

  • 所以在将端口号传入结构体中时(网络中使用的端口号就是从这个结构体中存放的),需要转换为网络字节序列

  • 同理,ip地址也需要传输,所以也需要转换为网络序列

  • (ip地址和端口号的选择在后面讲)

我们虽然使用了系统提供的类型,但这里的变量依然是在用户空间中定义的,并没有和内核中的套接字产生关联

  • 套接字需要这个结构体中的数据,将数据传进内核的套接字里,才叫做绑定
  • 前面的都只是预备工作,实际的绑定工作由bind函数完成 -- 通过网络文件描述符和结构体对象,将文件信息和网络信息相关联

以上,服务器端的套接字就建立完成

代码 
    void init(){// 创建套接字文件sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){lg(FATAL, "socket create error, sockfd : %d,%s", sockfd_,strerror(errno));exit(SOCKET_ERR);}// 创建sockaddr结构struct sockaddr_in addr;socklen_t len = sizeof(addr);bzero(&addr, len);addr.sin_addr.s_addr = inet_addr(ip_.c_str());addr.sin_family = AF_INET;addr.sin_port = htons(port_);// 绑定套接字信息int res = bind(sockfd_, reinterpret_cast<const struct sockaddr *>(&addr), len);if (res < 0){lg(FATAL, "bind error, sockfd : %d,%s", sockfd_,strerror(errno));exit(BIND_ERR);}lg(INFO, "bind success, sockfd : %d", sockfd_);}

服务器开始运行

思路

服务端负责获取客户端发送的数据

  • 使用recvfrom函数获得数据报形式的数据(也就是定义一个缓冲区)
  • 且拿到发送方的网络地址信息(结构体),方便将响应数据发回给对方

拿到数据后,进行处理

  • 这里在消息前封装一些标识字段 -- ip+端口号+发送时间

处理完后,就发回给发送方

  • 使用sendto函数发送数据报
代码
std::string generate_id(const std::string ip, const uint16_t port){return "[" + ip + ":" + std::to_string(port) + "]";}
std::string process_info(const std::string &info){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char time_stamp[SIZE];snprintf(time_stamp, sizeof(time_stamp), "[%d-%d-%d %d:%d:%d]:",ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);std::string id = generate_id(ip_, port_);std::string res = id + time_stamp + info + "\n";return res;}void run(){init();// 开始收发数据char buffer[buff_size];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in src_addr;socklen_t src_len = sizeof(src_addr);// 获取数据ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&src_addr), &src_len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::string id = generate_id(inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));message = id + "sever recvfrom success";lg(INFO, message.c_str());// 处理数据std::string echo_info = process_info(buffer);// 响应给发送端sendto(sockfd_, echo_info.c_str(), echo_info.size(), 0, reinterpret_cast<const struct sockaddr *>(&src_addr), src_len);message = id + "sever sendto success";lg(INFO, message.c_str());}}

注意点 -- ip地址和端口号的来源

ip地址的选择

可以使用默认ip,也可以自己手动传入

udp_server(const uint16_t port = 8080,const std::string ip = "0.0.0.0"): ip_(ip), port_(port), sockfd_(0){}
#include "udp_server.hpp"int main()
{udp_server s;//udp_server s(8080,"127.0.0.1");s.run();return 0;
}

这里如果服务端直接连接云服务器的公有ip,会报错:

  • 因为云服务器禁止直接绑定公网ip
  • 一台主机上可能会配置多个网卡,你看到的ip只是其中一个
  • 如果就固定地绑定了那一个,你的服务器就只能收到那一个ip地址收到的报文,其他ip收到的就看不到了
  • 所以,如果我们将ip地址写成0(也就是那个"0.0.0.0"),就可以将发往这台主机(无论是哪张网卡,它会动态识别是否属于该主机)的所有信息都向上交付给对应端口
  • 或者用宏 INIDDR_ANY
  • 虚拟机是可以直接绑定ip的
本地环回地址

这里的127.0.0.1指的是本地环回地址

  • 如果服务器绑定这个ip,就不会将数据发送到网络,而是贯穿网络协议栈后,重新发回本主机
  • 也就是本地通信
端口号

所以,我们一般用大一点的数字分配给我们的服务端

运行情况

#include "udp_server.hpp"
#include <cstdlib>
using namespace std;int main()
{udp_server s;// udp_server s(8080,"127.0.0.1");s.run();return 0;
}

netstat -nlup

  • n表示,把所有可以显示为数字的显示成数字,否则有些会是字符串
  • p表示,显示pid信息

用于检查网络状态和网络连接:

每列表示的含义:

  • 协议   收发报文的个数   本地ip地址+端口号  可以接收什么客户端的消息

只要可以查到信息,就说明这个服务器已经启动成功了

客户端

思路

和服务端的流程差不多,依然是封装成类,调用启动接口就可以启动客户端

启动后,就等待用户进行输入

初始化

思路

客户端也需要创建自己的套接字,但是不需要手动绑定

  • os会为我们自由分配一个端口号(客户端的端口号没有什么要求,只需要保证最基本的唯一性即可),且将本机ip和端口号与创建出的套接字绑定
  • os什么时候为我们绑定?
  • 首次发送消息的时候,也就是需要网络通信的时候再绑定

因为需要将输入的数据发给服务端,所以就需要提前知道服务端的套接字地址信息

  • 日常生活中,我们一般直接通过网址获得网站提供的服务

  • 网址经过处理后,就是ip地址,而端口号是固定的,一个ip地址就会有对应的固定端口号

  • 所以我们知道网址=客户端知道了ip地址=服务端的套接字被我们获取到

  • 但这里我们写的很简陋,只能手动提供服务端的ip和端口号,然后构建出服务端的网络地址信息(结构体)

(使用的接口还是那些)

代码 
struct data
{int sockfd_;struct sockaddr_in *paddr_;socklen_t len_;
};data *init(){// 创建套接字文件int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){lg(FATAL, "socket create error, sockfd : %d", sockfd);exit(SOCKET_ERR);}// 创建sockaddr结构struct sockaddr_in *svr_paddr = new sockaddr_in;socklen_t svr_len = sizeof(*svr_paddr);bzero(svr_paddr, svr_len);svr_paddr->sin_addr.s_addr = inet_addr(ip_.c_str());svr_paddr->sin_family = AF_INET;svr_paddr->sin_port = htons(port_);return new data({sockfd, svr_paddr, svr_len});}

客户端的运行

思路 

就是等待用户输入,然后发送给服务端

然后等待服务端的响应数据

最后将服务端处理后的数据打印出来

代码
void run(){data *d = init();std::string info;char buffer[buff_size];while (true){std::cout << "Please enter:";std::getline(std::cin, info);// 将消息发送给服务器sendto(d->sockfd_, info.c_str(), info.size(), 0, reinterpret_cast<const struct sockaddr *>(d->paddr_), d->len_);info.clear();struct sockaddr_in addr; // 仅用于填充参数,拿到自己的地址信息没啥意义socklen_t len = sizeof(addr);// 获取数据ssize_t n = recvfrom(d->sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&addr), &len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::cout << buffer << std::endl;memset(buffer, 0, sizeof(buffer));}}

运行情况 

#include "udp_client.hpp"
#include <cstdlib>
using namespace std;int main()
{udp_client c;//udp_client c(8080,"127.0.0.1");c.run();return 0;
}

运行情况 

这样我们就完成了cs互相通信的过程

改成命令行输入

上面我们是在代码中定义了ip地址和端口号,也可以以命令行的形式,将服务器的ip地址和端口号输入

  • 也就是利用main函数的参数
  • 只需要改一下主函数代码即可

服务端

#include "udp_server.hpp"
#include <cstdlib>
using namespace std;//./udp_server port
int main(int argc, char *argv[])
{if (argc != 2){std::cout << "./udp_server port(port>=1024)" << std::endl;exit(1);}udp_server s(atoi(argv[1]));s.run();return 0;
}

客户端

#include "udp_client.hpp"
#include <cstdlib>
using namespace std;//./udp_client ip port
int main(int argc, char *argv[])
{while (argc != 3){std::cout << "./udp_client ip port(port>=1024)" << std::endl;exit(1);}udp_client s(atoi(argv[2]), argv[1]); // port,ips.run();return 0;
}

运行情况

如果输入的内容不足,就会提示:

然后cs端都运行起来,并发送消息:

远程让服务器执行命令

引入

除了简单的转发消息,我们也可以发送命令,远程让服务器执行命令,且将执行结果传给客户端,让我们可以看到

  • 这不就是我们的shell吗
  • 其实之前有模拟过简单的shell(创建子进程,使用exec系列函数,让子进程去执行命令)

或者直接用popen函数

它会在内部帮助我们创建一条管道,且创建子进程+执行命令,然后把执行后的结果写入到管道文件中,以文件指针的形式返回给我们

command

  • 要执行的shell命令

mode

  • 表示要打开的管道的模式
  • 看你想要如何与命令交互:
  • 如果是只想要查看命令的输出,就以"r"打开 (ls)
  • 如果想要将一些数据发送给它作为输入,就以"w"打开 (cat)

分层

所以我们考虑改变一下代码结构,将处理数据与服务器收到数据解耦

  • 也就是在服务端处理数据时,使用自定义的处理方法(以回调的方式使用)

代码

服务端类里面修改的不多

在处理数据那里,我们直接调用参数:

void run(func_t func){init();// 开始收发数据char buffer[buff_size];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in src_addr;socklen_t src_len = sizeof(src_addr);// 获取数据ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&src_addr), &src_len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::string id = generate_id(inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));message = id + "sever recvfrom success";lg(INFO, message.c_str());// 处理数据std::string echo_info = func(buffer);// 响应给发送端sendto(sockfd_, echo_info.c_str(), echo_info.size(), 0, reinterpret_cast<const struct sockaddr *>(&src_addr), src_len);message = id + "sever sendto success";lg(INFO, message.c_str());}}

因为我们需要将处理函数放在类外,所以多增加了一个接口,用于外部调用

 static std::string get_id() //外部使用{udp_server obj;return obj.generate_id(obj.ip_, obj.port_);}private:std::string generate_id(const std::string ip, const uint16_t port){return "[" + ip + ":" + std::to_string(port) + "]";}

主函数这里多修改了一些:

  • 分出两个处理函数
  • 把获取时间的功能也单拎了出来
#include "udp_server.hpp"
#include <cstdlib>
#include <cstdio>
using namespace std;std::string get_time()
{time_t t = time(nullptr);struct tm *ctime = localtime(&t);char time_stamp[SIZE];snprintf(time_stamp, sizeof(time_stamp), "[%d-%d-%d %d:%d:%d]:",ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);return time_stamp;
}std::string process_info(const std::string &info)
{std::string time_stamp = get_time();std::string id = udp_server::get_id();std::string res = id + time_stamp + info;return res;
}std::string process_command(const std::string &cmd)
{std::string time_stamp = get_time();std::string id = udp_server::get_id();std::string res;FILE *fp = popen(cmd.c_str(), "r");if (fp == nullptr){lg(ERROR, "popen error, errno: %d, err string: %s", errno, strerror(errno));return "";}char buffer[buff_size];memset(buffer, 0, sizeof(buffer));if (fgets(buffer, sizeof(buffer) - 1, fp) == nullptr){res = id + time_stamp + "command error";return res;}while (true){memset(buffer, 0, sizeof(buffer));if (fgets(buffer, sizeof(buffer) - 1, fp) != nullptr){res += buffer;}else{break;}}return res;
}
int main()
{udp_server s;// udp_server s(8080,"127.0.0.1");s.run(process_command);return 0;
}

运行情况 

如果输入的不是命令,会提示给用户:

如果是命令,就会返回给我们运行结果:


文章转载自:
http://drupel.bwmq.cn
http://alchemic.bwmq.cn
http://agonising.bwmq.cn
http://hodman.bwmq.cn
http://weldable.bwmq.cn
http://cassegrain.bwmq.cn
http://cleansing.bwmq.cn
http://solemnify.bwmq.cn
http://grille.bwmq.cn
http://desirable.bwmq.cn
http://bejewel.bwmq.cn
http://tat.bwmq.cn
http://mendicity.bwmq.cn
http://cushy.bwmq.cn
http://rower.bwmq.cn
http://trisodium.bwmq.cn
http://sylvite.bwmq.cn
http://pedicab.bwmq.cn
http://combi.bwmq.cn
http://porous.bwmq.cn
http://loyalty.bwmq.cn
http://influencing.bwmq.cn
http://equitable.bwmq.cn
http://purpresture.bwmq.cn
http://shibilant.bwmq.cn
http://sporozoon.bwmq.cn
http://bir.bwmq.cn
http://rollock.bwmq.cn
http://extine.bwmq.cn
http://phenylamine.bwmq.cn
http://wynd.bwmq.cn
http://laical.bwmq.cn
http://orebody.bwmq.cn
http://loaner.bwmq.cn
http://homological.bwmq.cn
http://ecological.bwmq.cn
http://harsh.bwmq.cn
http://volute.bwmq.cn
http://galess.bwmq.cn
http://firebug.bwmq.cn
http://semisacred.bwmq.cn
http://hielamon.bwmq.cn
http://noyade.bwmq.cn
http://thigmotropism.bwmq.cn
http://immunodiffusion.bwmq.cn
http://shortsighted.bwmq.cn
http://phonochemistry.bwmq.cn
http://provable.bwmq.cn
http://pin.bwmq.cn
http://ubiquity.bwmq.cn
http://conterminous.bwmq.cn
http://moue.bwmq.cn
http://murdoch.bwmq.cn
http://rushy.bwmq.cn
http://geogeny.bwmq.cn
http://bra.bwmq.cn
http://avirulence.bwmq.cn
http://rainily.bwmq.cn
http://sequal.bwmq.cn
http://unaware.bwmq.cn
http://dolorology.bwmq.cn
http://manes.bwmq.cn
http://toup.bwmq.cn
http://headdress.bwmq.cn
http://spreading.bwmq.cn
http://dispiteous.bwmq.cn
http://hippy.bwmq.cn
http://peridiolum.bwmq.cn
http://pectic.bwmq.cn
http://fixer.bwmq.cn
http://workman.bwmq.cn
http://collate.bwmq.cn
http://bandjarmasin.bwmq.cn
http://psychoeducational.bwmq.cn
http://indumentum.bwmq.cn
http://lycian.bwmq.cn
http://wsj.bwmq.cn
http://thammuz.bwmq.cn
http://crowned.bwmq.cn
http://cordless.bwmq.cn
http://gunnery.bwmq.cn
http://quietist.bwmq.cn
http://nanning.bwmq.cn
http://moocha.bwmq.cn
http://clapham.bwmq.cn
http://buchmanism.bwmq.cn
http://dissyllabic.bwmq.cn
http://televisionless.bwmq.cn
http://silk.bwmq.cn
http://cheddite.bwmq.cn
http://seignory.bwmq.cn
http://sawbones.bwmq.cn
http://bursectomize.bwmq.cn
http://inherently.bwmq.cn
http://erectile.bwmq.cn
http://stoke.bwmq.cn
http://bureau.bwmq.cn
http://independent.bwmq.cn
http://omission.bwmq.cn
http://admittance.bwmq.cn
http://www.hrbkazy.com/news/83181.html

相关文章:

  • 可以做网站的编程有什么软件最简单的网页制作
  • 毕业设计做b2c网站的意义制作一个网站的费用是多少
  • 媒体门户网站建设方案北京做seo的公司
  • 本溪网站设计近期的新闻消息
  • 如何做镜像别人网站网络seo排名
  • 搬瓦工安装wordpress网络推广运营优化
  • 龙岗微信网站制作化妆培训
  • 国内有做外汇的正规网站吗seo咨询
  • 可以做初中地理题的网站清远头条新闻
  • 松江做移动网站企业微信scrm
  • 网站被屏蔽怎么访问游戏优化是什么意思
  • 网站建设外包公司seo和sem哪个工资高
  • 页面模板第三方应用独立站seo推广
  • 郓城网站开发企拓客app骗局
  • wordpress docker安装目录惠州seo按天计费
  • 上海企业建设网站电话google竞价推广
  • 夸克建站系统官网济南网站优化排名推广
  • 用ssh做网站怎么制作网址
  • 大连电子商务网站建设互联网推广公司靠谱吗
  • 网站建设品牌策划seo01
  • 曹县做网站建设百分百营销软件官网
  • wordpress 多站点主题搜狗网站
  • 网站制作教学google官网注册
  • 跟做竞价的网站友情链接有用吗seo编辑的工作内容
  • 营销型网站有什么特点nba排名最新排名
  • 网站建设报价单模板关键词优化难度查询
  • 用邮箱地址做网站域名好吗百度云搜索引擎入口盘多多
  • 什么不属于网站推广软件新app推广方案
  • 澳大利亚网站设计网站如何被百度快速收录
  • 西北建设有限公司网站自助发外链网站