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

网站建设流程笔记优质服务的小企业网站建设

网站建设流程笔记,优质服务的小企业网站建设,网站建设功能文档,上海响应式网站设计网络套接字 一、端口号1. 认识端口号2. socket 二、认识TCP协议和UDP协议1. TCP协议2. UDP协议 三、网络字节序四、socket 编程1. socket 常见API2. sockaddr 结构3. 编写 UDP 服务器#xff08;1#xff09;socket()#xff08;2#xff09;bind()#xff08;3#xff0… 网络套接字 一、端口号1. 认识端口号2. socket 二、认识TCP协议和UDP协议1. TCP协议2. UDP协议 三、网络字节序四、socket 编程1. socket 常见API2. sockaddr 结构3. 编写 UDP 服务器1socket()2bind()3recvfrom()4sendto()5udp 服务端和客户端 4. 地址转换函数1相关接口2关于 inet_ntoa 5. 编写 TCP 服务器1listen()2accept()3con4守护进程5tcp 服务端和客户端 一、端口号 1. 认识端口号 实际上我们两台机器在进行通信时是应用层在进行通信应用层必定会推动下层和对方的上层进行通信。 其实网络协议栈中的下三层主要解决的是数据安全可靠的送到远端机器。而用户使用应用层软件完成数据发送和接收的。那么用户要使用软件首先需要把这个软件启动起来所以软件启动起来本质就是进程所以两台机器进行通信本质是两台机器之上的应用层在通信也就是两个进程之间在互相交换数据所以网络通信的本质就是进程间通信只不过在网络通信中的公共资源是网络通过网络协议栈利用网络资源让两个不同的进程看到了同一份资源 在网络协议栈中在传输层怎么把数据正确交给上层应用层呢怎么知道交给哪一个应用呢所以就要求上层应用层和传输层之间必须协商一种方案让我们把数据准确交给上层这个方案我们称为端口号。所以在传输层的报头中必须要有原端口号和目的端口号也就是根据目的端口号就可以决定这个数据的有效载荷要交给上层应用的哪一个所以对于端口号无论对于客户端和服务端都能唯一的标识该主机上的一个网络应用层的进程 我们可以这样理解其实在传输层当中操作系统会形成一张哈希表哈希表中的类型是 task_struct*每一个应用层都要和该哈希表绑定端口号本质就是根据端口号在哈希表里做哈希运算如果该位置已经被占用了就不能被绑定了因为一个端口号只能被一个进程绑定如果该位置没有被使用就把该进程的pcb地址放在该位置上。 2. socket 因为在公网上IP地址 能表示唯一的一台主机端口号 port用来标识该主机上的唯一的一个进程所以 IP port 就可以标识全网唯一的一个进程那么我们在网络通信时只需要在对应的报头上填上原IP和目的IP原port和目的port就可以将报文交给另一个主机的进程这种基于 IP port 的通信方式我们称为 socket. 那么端口号和进程pid有什么区别呢进程pid也能标识一台主机上的唯一进程啊因为首先不是所有的进程都要通信但是所有的进程都要有pid其次是为了使系统和网络功能解耦 二、认识TCP协议和UDP协议 下面我们先认识一下两个传输层协议 1. TCP协议 此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识后面我们再详细讨论 TCP 的一些细节问题。 传输层协议有连接可靠传输面向字节流 2. UDP协议 此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识后面再详细讨论。 传输层协议无连接不可靠传输面向数据报 三、网络字节序 我们已经知道内存中的多字节数据相对于内存地址有大端和小端之分磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;接收主机把从网络上接到的字节依次保存在接收缓冲区中也是按内存地址从低到高的顺序保存;因此网络数据流的地址应这样规定:先发出的数据是低地址后发出的数据是高地址.TCP/IP协议规定,网络数据流应采用大端字节序即低地址高字节.不管这台主机是大端机还是小端机都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端, 就需要先将数据转成大端否则就忽略直接发送即可; 为使网络程序具有可移植性使同样的C代码在大端和小端计算机上编译后都能正常运行可以调用以下库函数做网络字节序和主机字节序的转换 这些函数名很好记h 表示 hostn 表示 networkl 表示 32 位长整数s 表示16位短整数例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序例如将IP地址转换后准备发送如果主机是小端字节序这些函数将参数做相应的大小端转换然后返回如果主机是大端字节序这些函数不做转换将参数原封不动地返回。 四、socket 编程 1. 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);2. sockaddr 结构 socket API 是一层抽象的网络编程接口适用于各种底层网络协议如IPv4、IPv6以及后面要讲的 UNIX Domain Socket然而各种网络协议的地址格式并不相同 IPv4 和 IPv6 的地址格式定义在 netinet/in.h 中IPv4地址用 sockaddr_in 结构体表示包括16位地址类型, 16位端口号和32位IP地址.IPv4、IPv6 地址类型分别定义为常数 AF_INET、AF_INET6这样只要取得某种 sockaddr 结构体的首地址不需要知道具体是哪种类型的 sockaddr 结构体就可以根据地址类型字段确定结构体中的内容socket API 可以都用 struct sockaddr* 类型表示在使用的时候需要强制转化成 sockaddr_in这样的好处是程序的通用性可以接收IPv4IPv6以及 UNIX Domain Socket 各种类型的 sockaddr 结构体指针做为参数。 3. 编写 UDP 服务器 1socket() 下面我们编写一个 UDP 服务器。首先需要做的是创建套接字使用到的接口是 socket() 第一个参数是我们创建的套接字的域即使用 IPv4 的网络协议还是 IPv6 的网络协议目前我们只需要关注这两个即可如下图 第二个参数表示当前 socket 对应的类型也就是相当于这个套接字未来给我们提供什么服务是面向字节流的还是面向用户数据报的如下 第三个参数表示的是协议类型目前我们不需要传这个参数。 而返回值相当于是一个文件描述符所以创建一个套接字的本质在底层就相当于是打开一个文件只不过以前的 struct file 指向的是键盘、显示器这样的设备而现在指向的是网卡设备。 2bind() 创建套接字成功之后接下来就要绑定端口号使用到的接口是 bind()如下 其中第一个参数就是创建套接字时的返回值第二个参数是一个结构体第三个参数是结构体的长度。但是我们在网络套接字编程的时候不用第二个参数类型的结构体这个结构体它只是设计接口用我们实际用的是 sockaddr_in 类型的结构体只需要在传参的时候进行强转即可。我们可以使用 bzero() 接口将该结构体清0 我们是要使用 bind 来让套接字和我们往该结构体中填充的网络信息要关联起来所以我们需要想该结构体中填充对应的字段。该结构体中有如下字段 对应下图 其中 sin_zero 为该结构体的填充字段也就是这些字段不用填充当作占位符即可sin_addr 代表 ip 地址sin_port 代表服务器所使用的端口号sin_family 代表该结构体对应的网络协议类型IPv4 或者 IPv6. 因为我们在给对方发送数据的时候我们也一定需要让对方知道我们是谁所以我们需要将端口号携带上发送给对方这样对方把数据处理完就可以给我们响应回来。所以端口号是要在网络里来回发送的也就是需要保证我们的端口号是网路字节序列因为该端口号是要给对方发送的。所以这里我们就需要用到主机序列转网络序列的接口由于端口号是两个字节所以用到的接口为 htons() 由于我们用户一般用的都是点分十进制字符串风格的 IP 地址也就是 0.0.0.0 这种风格每个点分的范围是 0~255每个字符一个字节远远超过结构体中要求的 32 位 ip 地址也就是四字节。所以我们需要将该字符串类型转换为 uint32_t 的类型那么用到的接口是 inet_addr()它的作用就是将字符串风格的 ip 地址转化为网络风格的 uint32_t 类型如下图 同端口号一样IP 地址也需要保证是网络字节序列。那么它的返回值类型 in_addr_t 其实就是符合网路字节序列的 uint32_t 的类型。 上面我们已经把准备工作做好了接下来我们就需要使用 bind() 接口进行绑定本质就是把我们定义的 struct 结构体设置进内核设置进指定的套接字内部。 3recvfrom() 接下来我们就需要在指定的一个套接字里获取数据内容使用到的接口是 recvfrom()如下图 第一个参数就是网络文件描述符第二个参数和第三个参数分别表示我们提供的缓冲区和它的长度读到的数据就会放在缓冲区中第三个参数设为0就是默认使用阻塞方式最后两个参数又是熟悉的结构体由于我们需要知道这些数据是谁给我们发的因为我们有可能也要将数据给对方返回。所以最后两个参数其实是输出型参数。 返回值成功就是对应的长度否则就是-1如下 4sendto() 将数据发送回给对方使用到的接口为 sendto()如下 参数和 recvfrom() 的参数类似这里不再介绍了。而最后两个参数是输入型参数我们要将数据发回给对方首先需要知道对方是谁而我们上面已经通过 recvfrom() 获取到了对方的结构体信息所以直接使用该结构体信息即可。 5udp 服务端和客户端 其中通过使用上面的接口编写的一个简单的接收客户端的字符串信息并进行简单的加工的 udp 服务器代码链接为UDP. 其中 udp server 的代码如下 #pragma once#include iostream#include string#include sys/types.h#include sys/socket.h#include netinet/in.h#include arpa/inet.h#include strings.h#include unistd.h#include cstring#include functional#include log.hppusing func_t std::functionstd::string(const std::string);//typedef std::functionstd::string(const std::string) func_t;std::string default_ip 0.0.0.0;uint16_t default_port 8080;log lg;class UdpServer{public:UdpServer(const uint16_t port default_port, const std::string ip default_ip): _port(port), _ip(ip), _isrunning(false), _sockfd(0){}void Init(){// 1.创建 udp 套接字_sockfd socket(AF_INET, SOCK_DGRAM, 0); // AF_INET PF_INETif (_sockfd 0){lg(Fatal, socket create faild, sockfd: %d, _sockfd);exit(1);}lg(Info, socket create success, sockfd: %d, _sockfd);// 2.绑定端口号// 2.1 准备数据struct sockaddr_in local;bzero(local, sizeof(local));local.sin_family AF_INET;local.sin_port htons(_port); // 主机序列转网络序列// local.sin_addr.s_addr inet_addr(_ip.c_str()); // 1.string - uint32_t 2.保证uint32_t是网络序列local.sin_addr.s_addr htonl(INADDR_ANY);// 2.2 开始bindint n bind(_sockfd, (const sockaddr *)local, sizeof(local));if (n 0){lg(Fatal, bind faild, errno: %d, err message: %s, errno, strerror(errno));exit(2);}lg(Info, bind success, errno: %d, err message: %s, errno, strerror(errno));}void Run(func_t func){_isrunning true;char buffer[1024];while (_isrunning){// 记录客户端发来时的结构体信息struct sockaddr_in client;socklen_t len sizeof(client);ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)client, len);if (n 0){lg(Warning, recvfrom error, errno: %d, err message: %s, errno, strerror(errno));continue;}buffer[n] 0;// 对数据进行简单的加工std::string info buffer;std::string echo_string func(info);// 发送回给对方sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (const sockaddr *)client, len);}}~UdpServer(){if (_sockfd 0)close(_sockfd);}private:int _sockfd;uint16_t _port;std::string _ip;bool _isrunning;};udp client 的代码如下 #include iostream#include cstdlib#include unistd.h#include strings.h#include sys/types.h#include sys/socket.h#include netinet/in.h#include arpa/inet.husing namespace std;void Usage(string proc){cout \n\rUsage: proc serverip serverport\n endl;}int main(int argc, char* argv[]){if(argc ! 3){Usage(argv[0]);exit(0);}string server_ip argv[1];uint16_t server_port stoi(argv[2]);sockaddr_in server;bzero(server, sizeof(server));server.sin_family AF_INET;server.sin_addr.s_addr inet_addr(server_ip.c_str());server.sin_port htons(server_port);socklen_t len sizeof(server);// client 也需要 bind只不过不需要用户显示 bind一般由OS自由随机选择// 系统会在首次发送数据的时候给我们bindint sockfd socket(AF_INET, SOCK_DGRAM, 0);if(sockfd 0){cout socker error endl;return 1;}string message;char buffer[1024];while(true){cout Plase Enter ;getline(cin, message);// 发送数据sendto(sockfd, message.c_str(), message.size(), 0, (sockaddr*)server, len);// 当服务器进行简单的加工处理后会发送回来此时客户端再次获取sockaddr_in temp;socklen_t size sizeof(temp);ssize_t s recvfrom(sockfd, buffer, 1023, 0, (sockaddr*)temp, len);if(s 0){buffer[s] 0;cout buffer endl;}}close(sockfd);return 0;}main 函数 #include iostream#include vector#include memory#include cstdio#include UdpServer.hppusing namespace std;void Usage(string proc){cout \n\rUsage: proc port[1024]\n endl;}// 处理字符串的方法string Handler(const std::string str){string res Server get a message: ;res str;cout res endl;return res;}// 远程执行指令的方法string ExcuteCommand(const string cmd){FILE* fp popen(cmd.c_str(), r);if(nullptr fp){perror(popen);return error;}string result;char buffer[4096];while(true){char* tmp fgets(buffer, sizeof(buffer), fp);if(tmp nullptr) break;result buffer;}pclose(fp);return result;}int main(int argc, char* argv[]){if(argc ! 2){Usage(argv[0]);exit(0);}uint16_t port stoi(argv[1]);unique_ptrUdpServer svr(new UdpServer(port, 127.0.0.1));svr-Init();svr-Run(ExcuteCommand);return 0;}有关代码中的细节 有关 IP 地址 云服务器禁止直接bind公网ipbind ip 地址为0表示的含义是任意地址绑定这种是比较推荐的做法。当 IP 地址为 127.0.0.1 时表示进行的是本地传输测试不会进行跨网传输。 有关 port 其中 0~1023 的端口号是系统内定的端口号一般都要有固定的应用层协议使用例如 http:80https:443所以我们一般绑端口号一般绑1024以上的。 popen() 系统调用 popen() 是一个被封装起来的管道和子进程执行命令的应用。 它的第一个参数就是需要执行的命令在底层它会帮我们进行 fork() 创建子进程并让父子进程建立管道然后让子进程把它的运行结果通过管道再返回给调用方。如果调用方想得到 command 指令的运行结果可以通过文件指针的方式读取。第二个参数相当于是打开这个命令的方式我们使用 “r” 即可。使用完毕后使用 pclose() 关闭该文件指针即可。 其中我们可以使用 netstat -nlup 查看系统中所有的 udp 信息并且把进程信息也显示出来。 我们还可以将以上代码修改成为多线程代码链接为多线程UDP. 4. 地址转换函数 1相关接口 我们只介绍基于 IPv4 的 socket 网络编程sockaddr_in 中的成员 struct in_addr sin_addr 表示32位 的 IP 地址但是我们通常用点分十进制的字符串表示 IP 地址以下函数可以在字符串表示和 in_addr 表示之间转换。我们在上面的 bind() 中也使用了地址转换函数 inet_addr(). 字符串转 in_addr 的函数 #include arpa/inet.hint inet_aton(const char* strptr, struct in_addr* addrptr);in_addr_t inet_addr(const char* strptr);int inet_pton(int family, const char* strptr, void* addrptr);in_addr 转字符串的函数 char* inet_ntoa(struct in_addr inaddr);const char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len);其中 inet_pton 和 inet_ntop 不仅可以转换 IPv4 的 in_addr还可以转换 IPv6 的 in6_addr因此函数接口是 void* addrptr. 2关于 inet_ntoa inet_ntoa 这个函数返回了一个 char*很显然是这个函数自己在内部为我们申请了一块内存来保存 ip 的结果那么是否需要调用者手动释放呢 man 手册上说inet_ntoa 函数是把这个返回结果放到了静态存储区。这个时候不需要我们手动进行释放。 5. 编写 TCP 服务器 1listen() TCP 是面向连接的服务器一般是比较被动的所以服务器一直处于一种等待连接到来的状态这个工作叫做监听状态使用到的接口是 listen()如下 第一个参数为指定的套接字通过该套接字等待新连接的到来。第二个参数我们后面再介绍暂时设为10左右即可。返回值成功返回0失败返回-1. 2accept() 因为 TCP 是面向连接的所以在正式通信之前先要把连接建立起来使用到的接口为 accept()该接口的作用是获取一个新的连接如下 第一个参数为我们刚刚设置为监听状态的套接字后两个参数和 recvfrom() 的后两个参数一样都是输出型参数也就是谁给我们发的 TCP 报文那么对应的套接字信息就会通过这两个参数返回出来。 而返回值成功返回一个文件描述符否则返回-1那么返回值也是一个文件描述符我们原本也有一个文件描述符为什么会有两个 sockfd 呢我们该用哪个呢其实它们分工是明确的我们原本定义的 sockfd即被创建的被 bind 的被监听的套接字它的工作是从底层获取新的连接而未来真正提供通信服务的是 accept() 返回的套接字 至此我们可以使用 telnet 进行指定服务的一个远程连接后面跟上 IP 地址和端口号即可它在底层默认使用的就是 TCP. 3con 由于在 TCP 中客户端是要连接服务器的所以服务端需要有一个能够向服务器发起连接的接口该接口为 connect()如下 该接口的作用是通过指定的套接字向指定的网络目标地址发起连接。后两个参数和 sendto() 的后两个参数一样。返回值成功返回0失败返回-1. TCP 客户端也需要 bind但是和 UDP 一样不需要显示的 bind系统会在客户端发起 connect 的时候进行自动随机 bind. 我们可以使用 netstat -nltp 查看系统中所有 TCP 的信息并把进程信息显示出来。 4守护进程 在我们登录 Linux 的时候Linux 系统会给我们形成一个会话而且会为每个会话创建一个 bash 进程这个 bash 就可以为用户提供命令行服务。每个会话中只能存在一个前台进程但是可以存在多个后台进程而键盘信号只能发送给前台进程。前台和后台进程的区别就是是否拥有键盘文件它们都可以向显示器打印而只有前台进程才能从键盘即标准输入获取数据 如果我们不想后台进程向显示器打印的数据影响我们我们可以将它的打印数据重定向到文件中例如 其中 [1] 表示后台任务号后面数字表示进程 PID. 而查看后台任务的指令为jobs如下 如果我们想把后台进程提到前台可以使用 fg 任务号如下 如果想把它重新放回后台我们可以使用 ctrl z 将该进程暂停。然后使用 bg 任务号 将该进程重新启动如下 接下来我们再运行几个后台进程例如使用 sleep方便观察 Linux 中的进程间关系使用 ps axj | head -1 ps axj | grep -Ei a.out|sleep 查看它们的进程信息 其中 PPID、PID 我们都认识而 PGID 表示的是进程组IDSID 表示 session id即会话 id. 而系统中可能会存在多个 session所以系统需要管理多个 session. 我们可以看到./a.out 进程的 PID 和 PGID 是一样的所以它就是自成进程组的。而三个 sleep 分别是三个不同的进程但是它们的 PGID 却是同一个而且是用管道建立的进程的第一个进程的 PID所以它们三个自成一组而组长是多个进程中的第一个。那么进程组和任务有什么关系呢任务是要指派给进程组的所以我们需要校正一下以前的说法我们把前台进程称为前台任务后台进程称为后台任务因为可能某一个后台任务里面可能会包含多个进程。但是无论有几个进程组完成对应的任务在同一个会话内启动的SID 是一样的那么上面中的 SID 到底是谁呢我们可以查看一下 如上图我们可以看到它是 bash所以就是以 bash 的 pid 去构建了一个 session 这种后台进程会收到用户登录和退出的影响如果我们不想受到任何用户登录和注销的影响我们可以将进程守护进程化。什么是守护进程呢我们把自成进程组自成会话的进程称为守护进程那么我们该如何做到呢下面我们认识一个接口setsid()如下 该接口的作用就是哪个进程调用该接口就把该进程的组ID设置为会话ID也就是让进程独立成会话。 返回值成功返回进程的ID否则返回-1. 注意该接口不能由进程组的组长直接调用那么怎么才能保证不是组长调用呢所以我们可以使用 fork() 创建子进程调用所以守护进程的本质也是孤儿进程 5tcp 服务端和客户端 接下来我们结合上面所学的知识编写一个 TCP 服务器并将它守护进程化代码链接 其中在守护进程中我们的代码中是充满大量的打印的而这些打印默认是向标准输出打的也就是向显示器上打了而对于守护进程来说就不应该向显示器上打了所以我们需要一个解决方案。而 Linux 中存在一种字符文件叫做 /dev/null只要我们向该文件写入都会被该文件丢弃掉如果我们向该文件读取什么也读取不到。所以我们只需要将所有的输出向该文件写入即可。我们也可以将打印信息写入文件中。 另外TCP 在通信时是全双工的也就是可以同时读写的。在底层操作系统给 TCP 提供两个缓冲区一个发送缓冲区一个接收缓冲区我们在用 TCP 的同时别人也在用所以别人也会有上面两个缓冲区所以当我们发送数据是先把我们的数据拷贝到我们的 TCP 的发送缓冲区然后通过网络会发送到对方的接收缓冲区反过来也同理如下图
http://www.hkea.cn/news/14276049/

相关文章:

  • 互联网如何做旅游网站佛山网约车租赁公司
  • 个人网站可以做企业网站吗手机网站logo
  • 做凉菜的网站wordpress怎么缩短标题
  • 电子商务网站规划与建设试题网站建设研究背景
  • 网站开发与设计武威 网站建设
  • 公司网站制作的费用申请做爰视频网站在线看
  • 柳江企业网站建设价格软件技术属于什么学类
  • 男女做爰视频网站在线视频如何做网站免费搭桥链接
  • 做网站 绍兴展览公司网站建设
  • 网站修改图片怎么做龙岩相亲网
  • 湖南星大建设集团有限公司网站网店营销
  • 网站 建设 现状分析新冠怎么突然不见了
  • 做网站公司上班违法吗网站设计与程序方向
  • 西安商城网站建设网站专业术语中seo意思是
  • 成都电商网站开发网络做网站如何盈利
  • 制作企业网站页面多少钱wordpress js 调用图片
  • 网站开发技术应用领域thinkphp 做网站如何
  • 游戏网站建设论文简单漂亮的博客php网站源码
  • 医院网站建设安全协议哪个建站软件比较好带论坛
  • 怎么对网站做seo优化二维码生成器加logo
  • 没有固定ip做网站n怎样建立自己的网站
  • 怎么攻击网站吗旅游最新政策
  • 本人找做钢筋笼的活网站wordpress认证机制
  • 未成年人思想道德建设网站今天郑州最新通告
  • 做网站常用什么软件dedecms中英文网站 模板
  • 建设小型网站系统开题报告用帝国做的网站
  • 河北省城乡与住房建设厅网站牛商网朱依静
  • 网站开发大揭秘wordpress关闭缓存
  • 网站建设兼职合同模板seo推广软件费用
  • 买了云服务器怎么做网站普陀做网站