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

什么软件能把做的网站上传一键制作网站

什么软件能把做的网站上传,一键制作网站,专门做餐饮空间设计的网站,企业名录2019企业黄页目录 一、socket 编程接口 1.1 sockaddr 结构 1.2 socket 常见API 二、封装 InetAddr 三、网络字节序 四、封装通用 UdpServer 服务端 4.1 整体框架 4.2 类的初始化 4.2.1 socket 4.2.2 bind 4.2.3 创建流式套接字 4.2.4 填充结构体 4.3 服务器的运行 4.3.1 rec…

目录

一、socket 编程接口

1.1 sockaddr 结构

1.2 socket 常见API

二、封装 InetAddr

三、网络字节序

四、封装通用 UdpServer 服务端

4.1 整体框架

4.2 类的初始化 

4.2.1 socket

4.2.2 bind

4.2.3 创建流式套接字

4.2.4 填充结构体

4.3 服务器的运行 

4.3.1 recvfrom

4.3.2 sendto

4.3.3 接收数据

4.3.4 发送数据

4.4 UdpServer.hpp

五、封装通用 UdpClient 客户端


OSI 参考模型与 TCP/IP 分层模型的对比

一、socket 编程接口

1.1 sockaddr 结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、 IPv6、UNIX Domain Socket。然而, 各种网络协议的地址格式并不相同:

它们都定义在 netinet/in.h 中,其中,

struct sockaddr

  • 它是一个通用的套接字地址结构体,通常在需要传递通用地址结构体指针的地方使用。
  • 定义:
    struct sockaddr 
    {unsigned short sa_family;    // 地址族char sa_data[14];            // 地址数据
    };
    
  • sa_family 指定地址族,比如 AF_INETAF_UNIX 等。

struct sockaddr_in

  • 它专门用于 IPv4 地址的套接字编程。
  • 定义:
    struct sockaddr_in 
    {short int sin_family;        // 地址族 (AF_INET)unsigned short int sin_port; // 端口号struct in_addr sin_addr;     // IP 地址unsigned char sin_zero[8];   // 填充,使结构体大小与 `struct sockaddr` 一致
    };struct in_addr 
    {unsigned long s_addr;        // 32 位的 IP 地址
    };
    
  • sin_family 通常为 AF_INET,表示使用 IPv4;AF_INET6,表示使用 IPv6 
  • sin_port 存储端口号,使用 htons 函数转换为网络字节序
  • sin_addr 存储 IPv4 地址,使用 inet_addrinet_pton 函数进行设置。
  • in_addr中的 s_addr初始化时使用 INADDR_ANY

struct sockaddr_un

  • 它专门用于 UNIX 域套接字编程。
  • 定义:
    struct sockaddr_un 
    {sa_family_t sun_family;      // 地址族 (AF_UNIX)char sun_path[108];          // 路径名
    };
    
  • sun_family 通常为 AF_UNIX,表示使用 UNIX 域套接字。
  • sun_path 存储文件系统路径名,表示套接字文件的位置。

IPv4、 IPv6地址类型分别定义为常数AF_INET、 AF_INET6。这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容

socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。

1.2 socket 常见API

套接字是通信的端点,允许在网络上的两个主机之间进行数据传输。

每个套接字都与一个特定的地址和端口绑定,以标识唯一的通信端点。

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

二、封装 InetAddr

InetAddr 将一套接字结构体的服务号与端口号封装成类,以后调用某一套接字的IP地址与端口号时就可以直接使用语言层面的一些数据类型,如 string 、uint16_t ,这样比较统一。

因为我们传入的 ip 地址是 "xxx.xxx.xxx.xxx" ,这是一个 string 类,在服务端的 main 函数中,可以使用 inet_addr 将其传入 struct addr_in 的 s_addr 中。

class InetAddr
{
private:struct sockaddr_in _addr;string _ip;uint16_t _port;
};


接下来,就是类的成员函数,保证类可以返回 sockaddr_in \ ip \ port 即可。
但是,需要注意的是,struct sockaddr_in 中的 IP 地址与端口号与我们定义的类型不同,系统中也提供了相应的函数便于我们的转化, ntohs 与 inet_ntoa 前者用于网络字节序转化为主机字节序,后者用于将网络字节顺序给出的主机地址转化为IPv4点分十进制的字符串。

void GetAddr()
{_ip = inet_ntoa(_addr.sin_addr.s_addr);_port = ntohs(_addr.sin_port);
}
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void GetAddress(){_port = ntohs(_addr.sin_port);_ip = inet_ntoa(_addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr) : _addr(addr){GetAddress();}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};

三、网络字节序

内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

换句话说,如果从一个大端存储的计算机传输数据至一个小端存储的计算机,那么如果网络层不进一步优化的话,传过去的数据不就都乱套了吗。

发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可。

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include <arpa/inet.h>
uint32_t htonl(uint32_t, hostlong);
uint16_t htons(uint16_t, hostshort);
uint32_t ntohl(uint32_t, netlong);
uint16_t ntohs(uint16_t, netshort);

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

四、封装通用 UdpServer 服务端

服务端要负责的是接收客户端的请求并给予客户端一定的响应,对 UdpServer 的封装要包括套接字的建立、服务的绑定、数据的收集、数据的传输等。

4.1 整体框架

首先,我们知道每个Mac都有其独特的IP,那么Mac中有那么多的应用软件,应该如何才能确定当前服务需要在哪个应用中呢?这就引入了端口号,用于标识一台Mac中唯一的应用。所以在UdpServer中,不仅要创建流式套接字,还要有唯一的端口号。除此之外,如果在外部需要停止服务端的响应,可以设置一个布尔类型的变量来标识UdpServer是否在运行。

其次,在编写通用的 UdpServer 类时,构造函数通常不会将套接字文件描述符 (sockfd) 作为参数进行传递。这是因为套接字文件描述符是在类的内部创建和管理的,而不是由外部提供。

#include <iostream>static const int gdefaultsockfd = -1;
class UdpServer
{public:UdpServer(uint16_t port):_sockfd(gdefaultsockfd), _port(port), _isrunning(false){}
private:int _sockfd;uint16_t _port;bool _isrunning;
};

4.2 类的初始化 

上面我们提到编写通用的 UdpServer类时,构造函数通常不需要传入 sockfd ,在后面会将的 TcpServer 也是如此,所以在初始化函数时,就要对套接字进行创建以及与 sockaddr 的绑定。

4.2.1 socket

socket 函数用于创建一个新的套接字。套接字是网络通信的端点。 

#include <sys/types.h>      
#include <sys/socket.h>int socket(int domain, int type, int protocol);

传入参数

  • domain: 指定协议族,如 AF_INET(IPv4)或 AF_INET6(IPv6)等。
  • type: 指定套接字类型,如 SOCK_STREAM(TCP)或 SOCK_DGRAM(UDP)。
  • protocol: 通常为 0,表示自动选择合适的协议。如果需要特定协议,可以传递协议编号。

返回值

    成功时返回一个文件描述符,失败时返回 -1,并设置 errno 来指示错误。

4.2.2 bind

bind 函数将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

传入参数

  • socket: 由 socket 函数返回的套接字文件描述符。
  • address: 指向一个 struct sockaddr 类型的指针,包含要绑定的地址信息。
  • address_len: 地址结构体的长度。

返回值

成功时返回 0,失败时返回 -1,并设置 errno 来指示错误。

4.2.3 创建流式套接字

    void UdpInit(){// 1.创建流式套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno); exit(SOCKET_ERROR);}}

我们带入了上一节中的日志宏,同时因为 socket 函数可以带出错误信息,所以当套接字创建失败是,可以使用 strerror 打印一下错误信息,并可以通过枚举使 exit 时的信息更明确:

#include <cstring>
#include "Log.hpp"enum
{SOCKET_ERROR = 1,BIND_ERROR,USAGE_ERROR
}; 

4.2.4 填充结构体

这里使用的是 struct sockaddr_in 结构体,首先把结构体成员都初始化为0,这里使用 bezero 函数,sockaddr_in 结构体中的各个成员对 sin_family\sin_addr.s_addr 初始化,初始化的参数详见1.1 sockaddr 结构,然后向其中的 sin_port 填充我们输入的端口号。

        // 2.0创建struct sockaddr_in 并填充struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_port);// 2.1 bind 将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");
    void InitServer(){// 1.创建流式套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno); // strerror->#include<string.h>exit(SOCKET_ERROR);}// 2.0创建struct sockaddr_in 并填充struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_port);// 2.1 bind 将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}

4.3 服务器的运行 

首先,我们希望服务器一直运行,所以需要设置死循环。其次,服务器进行收发信息要使用到函数recvfrom 与 sendto

4.3.1 recvfrom

recvfrom 函数用于从一个UDP套接字接收数据。它可以用于接收来自任意地址的数据,因此特别适合于UDP服务器。

#include <sys/types.h>
#include <sys/socket.h>ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len);

传入参数

  • socket: 套接字文件描述符,由 socket 函数返回。
  • buffer: 用于存储接收到的数据的缓冲区指针。
  • length: 缓冲区的长度。
  • flags: 通常为 0,也可以是一些控制操作行为的标志,例如 MSG_DONTWAIT(非阻塞操作)。
  • address: 指向 struct sockaddr 的指针,用于存储发送数据的源地址
  • address_len: 指向 socklen_t 的指针,指示 address 的大小,并在函数返回时设置为实际地址的长度。

返回值

成功时返回接收到的数据字节数,失败时返回 -1,并设置 errno 来指示错误。

4.3.2 sendto

sendto 函数用于通过一个UDP套接字发送数据。它可以用于发送数据到指定的地址,因此特别适合于UDP客户端和服务器。

#include <sys/types.h>
#include <sys/socket.h>ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);

传入参数

  • socket: 套接字文件描述符,由 socket 函数返回。
  • message: 指向要发送的数据缓冲区的指针。
  • length: 要发送的数据的长度。
  • flags: 通常为 0,也可以是一些控制操作行为的标志,例如 MSG_DONTWAIT(非阻塞操作)。
  • dest_addr: 指向 struct sockaddr 的指针,包含目标地址信息
  • dest_len: 目标地址结构体的长度。

返回值

成功时返回发送的数据字节数,失败时返回 -1,并设置 errno 来指示错误。

4.3.3 接收数据

首先,所有的操作都要定义在一个 while 的死循环中。其次,因为 recvfrom 中需要使用缓冲区,所以还要定义一个缓冲区。同时, recvfrom 可以标明发送数据的源地址,所以可以定义一个 sockaddr_in 的结构体,用于存储发送数据的源地址,当接收成功时,可以使用之前定义的 InetAddr 类来接收该源地址。

    void Start(){_isrunning = true;while (_isrunning){char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&peer, &len);if (n > 0){buffer[n] = 0;InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);}}_isrunning = false;}

4.3.4 发送数据

既然已经接收到数据了,我们需要让客户端知道服务端已经接收到了数据,所以当接收数据成功时,在使用 sendto 发送数据至客户端。 

    void Start(){_isrunning = true;while (_isrunning){char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&peer, &len);if (n > 0){buffer[n] = 0;InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);}}}

以下是为什么服务端需要接收数据还需要传输到客户端的原因,究其原因还是与Udp最初的开发有关:

在 UDP 服务器接收到客户端的信息后使用 sendto 函数发回响应,是为了实现双向通信,使得客户端可以知道服务器已经正确接收到并处理了请求。以下是这种设计背后的主要原因:

1. 确认信息接收

在无连接的 UDP 协议中,数据包的发送和接收是独立的,且没有内建的机制来确认数据包是否成功到达对方。通过服务器发回一个响应,客户端可以确认其发送的信息已经被接收到并处理。

2. 双向通信

多数网络应用需要双向通信,不仅客户端需要向服务器发送数据,服务器也需要向客户端发送数据。比如,客户端发送请求数据,服务器处理后返回相应的结果。这种交互模式在很多应用场景中都是必须的。

3. 应用层协议实现

通过在应用层协议中定义请求-响应模式,可以更好地实现和管理通信过程。服务器接收到请求后返回响应,是许多协议(例如 DNS、DHCP 等)基本工作方式的一部分。

4. 保持通信会话

在某些应用中,客户端和服务器需要保持持续的通信会话。服务器向客户端发回响应,可以作为会话的一部分,确保双方在同一上下文中进行通信。

4.4 UdpServer.hpp

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include "Log.hpp"
#include "InetAddr.hpp"enum
{SOCKET_ERROR = 1,BIND_ERROR,USAGE_ERROR
};static const int gdefaultsockfd = -1;
class UdpServer
{
public:UdpServer(uint16_t port) : _sockfd(gdefaultsockfd), _port(port), _isrunning(false){}void InitServer(){// 1.创建流式套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno); // strerror->#include<string.h>exit(SOCKET_ERROR);}// 2.0创建struct sockaddr_in 并填充struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_port);// 2.1 bind 将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}void Start(){_isrunning = true;while (_isrunning){char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);if (n > 0){buffer[n] = 0;InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);}}}~UdpServer(){}private:int _sockfd;uint16_t _port;bool _isrunning;
};

如果以后有其他的业务,可以在类内定义一个回调函数成员指针或者使用 function 封装一个回调函数,在构造函数中传入该回调函数,并在 Start 中执行相应的回调函数即可,大致思路如下,具体改动的是 Start 中 sendto 的传入参数。

using func_t = std::function<std::string(const std::string&, bool &ok)>;
class UdpServer
{
public:UdpServer(uint16_t port, func_t func) : _sockfd(defaultfd), _port(port), _isrunning(false), _func(func){}void Start(){while (){if (){std::string response = _func(request, ok); sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&peer, len);}}}
private:int _sockfd;uint16_t _port; bool _isrunning;// 给服务器设定回调,用来让上层进行注册业务的处理方法func_t _func;
};

五、封装通用 UdpClient 客户端

在 C/C++ 中,argcargv 是命令行参数的标准输入参数,用于在程序启动时获取命令行参数。

  • argc (argument count): 表示命令行参数的个数,包括程序名本身。
  • argv (argument vector): 是一个字符指针数组,包含了命令行输入的参数。argv[0] 通常是程序的名称argv[1]argv[argc-1] 是实际的命令行参数。

当程序正确启动时,应输入以下参数

./UdpClient 127.0.0.1 8080
  • argc 的值为 3。
  • argv 的内容如下:
    • argv[0]"./UdpClient",程序名。
    • argv[1]"127.0.0.1",服务器 IP。
    • argv[2]"8080",服务器端口。
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 1. 创建socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){std::cerr << "socket error" << std::endl;}// 构建目标主机的socket信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());std::string message;// 2. 直接通信即可while(true){std::cout << "Please Enter# ";std::getline(std::cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));struct sockaddr_in peer;socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if(n > 0){buffer[n] = 0;std::cout << "server echo# " << buffer << std::endl;}}return 0;
}
http://www.dinnco.com/news/55383.html

相关文章:

  • 网页编辑软件edit郑州seo优化哪家好
  • 品牌工厂网站建设重庆百度开户
  • 雅虎网站收录提交入口seo自学网站
  • 湖南宁乡建设局网站百度广告怎么投放
  • 中国建筑网官网查询人员证书查乐天seo培训中心
  • 网站开发商城实例做公司网页
  • 网站导航类型网络营销推广公司网站
  • 如何制作网站设计网站优化快速排名软件
  • 安徽公司网站建设今天最新新闻10条
  • 2016企业网站建设方案深圳百度seo整站
  • 深圳网站建设 网站设计百度知道官网手机版
  • 旅游网站建设的方向微信推广平台怎么做
  • java做网站的软件6惠州seo关键字排名
  • 常用的营销方法和手段抚顺优化seo
  • 泉州网站建设技术托管百度登录账号首页
  • 做汽车销售要了解的网站长沙网站优化效果
  • 河南省城市建设网站汕头seo排名
  • 重点建设学科网站短视频获客系统
  • 网站前台的网址电脑速成班短期电脑培训班
  • 郑州做订货网站安徽百度关键词优化
  • php做网站的分站湖南网络推广公司大全
  • wordpress添加广告代码seo百度排名优化
  • 网站建设指导三只松鼠软文范例500字
  • 重庆交通建设监理协会网站百度快速收录权限域名
  • 为什么做金融网站犯法百度做推广一般要多少钱
  • wordpress 判断文章页seo公司培训课程
  • 一站式服务英文农产品品牌推广方案
  • 做外贸是否需要有自己的网站国内做网站比较好的公司
  • wordpress logo不显示seoul是韩国哪个城市
  • 做电力公司网站百度指数的主要功能有