如何自己建立一个网站顺德佛山做app网站
- 作者: 五速梦信息网
- 时间: 2026年03月21日 09:50
当前位置: 首页 > news >正文
如何自己建立一个网站,顺德佛山做app网站,网站如何做实名认证,什么是网站?一、本章重点
- tcp服务器实现思路#xff0c;进一步了解和总结相关的接口
- 了解日志和守护进程 二、tcp服务器核心思路 tcp版的服务器与udp的不同在于#xff0c;udp是面向数据报传输数据#xff0c;在数据传输中不需要建立与客户端的链接#xff0c;直接用recvfrom…一、本章重点
- tcp服务器实现思路进一步了解和总结相关的接口
- 了解日志和守护进程 二、tcp服务器核心思路 tcp版的服务器与udp的不同在于udp是面向数据报传输数据在数据传输中不需要建立与客户端的链接直接用recvfrom和sendto这两个接口进行消息的收发而tcp版的服务器则是面向字节流的需要与客户端建立连接
- 创建监听套接字listen_sock 这个监听套接字是用于监听的监听就可以看做为等待客户端连接监听套接字只负责与客户端建立起连接然后剩下的任务交给其他的套接字去执行 // 1. 创建ListenSock_listensock socket(AF_INET, SOCK_STREAM, 0);if (_listensock 0){std::cerr create socket error std::endl;exit(SOCKET_ERR);} 2. 创建好struct sockaddr_in 并将其与套接字bind 这里和udp是一样的步骤 // 2. bindstruct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_addr.s_addr htonl(INADDR_ANY);local.sin_family AF_INET;local.sin_port htons(_port);if (bind(_listensock, (struct sockaddr *)local, sizeof(local)) 0){std::cerr bind error std::endl;exit(BIND_ERR);} 3. 监听 – listen //? if (listen(_listensock, backlog) 0){std::cerr listen socket error std::endl;exit(LISTEN_ERR);} 4. 获取连接 – accept 获取连接其实就是获取客户端的连接它会返回一个套接字后续的业务处理则是根据这个套接字去实现和完成的 // 1. 获取连接 – accept struct sockaddr_in client; socklen_t len sizeof(client); int sock accept(_listensock, (struct sockaddr )client, len); if (sock 0) {std::cerr sock error std::endl;exit(SOCKET_ERR); }5. 开展业务处理. 具体开展的业务处理要采用多进程或者多线程的方式主线程负责监听连接而其他线程负责为客户端提供具体服务。tcp的读写可以直接使用以往的文件相关的读写函数如read、write等等 // 3. 开展业务处理 – service// 测试std::cout 连接成功 sock from _listensock , client_ip - client_port std::endl;// service(sock,client_ip,client_port);// 业务处理需要和监听同时进行因为这里的业务处理是阻塞等待客户端的// 3.1 多进程方案 – 如何处理进程等待的阻塞问题// pid_t id fork();// if (id 0)// {// close(sock);// continue;// ;// }// else if (id 0) // child// {// close(_listensock);// service(sock, client_ip, client_port);// exit(0);// }// close(sock); – 线程方案中不能关闭这个// 进程等待 – waitpid// 方案一信号忽略 – signal(SIGCHLD, SIG_IGN);推荐// 方案二信号捕捉 – signal(SIGCHLD, handler);麻烦不太推荐// 方案三轮询等待 – WNOHANG不太推荐// 方案四孤儿进程 – if(fork() 0) exit(0);不太推荐但思路很优秀但是会对系统有负担// pid_t ret waitpid(id, nullptr, 0);// if (ret id)// std::cout wait child: id success std::endl;// 3.2 多线程方案 – 线程池优化// pthread_t tid;// ThreadData td new ThreadData(sock,client_ip,client_port,this);// pthread_create(tid,nullptr,threadRoutine,td);Task t(sock, client_ip, client_port, std::bind(TcpServer::service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));//?ThreadPoolTask::getinstance()-pushTask(t);三、Socket编程相关接口整理
- 网络编程相关的头文件 #includesys/types.h // 包含很多系统数据类型 #includesys/socket.h // 包含了基本的 socket 函数和数据结构定义。 #includenetinet/in.h // 定义了 Internet 地址族相关的结构和函数。 #includearpa/inet.h // 提供 IP 地址转换函数。 2. socket – 创建套接字函数 int socket(int domain, int type, int protocol); domain指定通信域常见的有AF_INETIPv4网络协议和AF_INET6IP6网络协议。 type指定套接字类型常见的有SOCK_STREAM面向字节流对应TCP和SOCK_DGRAM面向数据报对应UDP protocol通常设置为0表示使用默认协议。 返回值: 一般是文件文件描述符fd若失败则返回小于0的值 3. struct sockaddr_in sin_family第一行的宏转换后就是这个它指定的是地址族对于IPv4就设置为AF_INET sin_port端口号要以网络字节序去表示所以这里一般在填写的时候还要配合网络字节序的接口htons sin_addr里面封装的内容实际就是IP地址可以使用inet_addr 或 inet_pton函数将点分十进制的 IP 地址字符串转换为合适的格式并存储在 sin_addr.s_addr中。当然这里由于我们用的是云服务器前面提到云服务器的IP地址不能填唯一确定的而是这里需要填INADDR_ANY
- bind 绑定套接字和struct sockaddr的函数接口 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); sockfd 是通过socket函数创建的套接字描述符。 addr该结构体的解释在知识点三和知识点四有讲解对于 IPv4 通常是struct sockaddr_in类型对于 IPv6 是struct sockaddr_in6类型。这个地址结构包含了要绑定的 IP 地址和端口号等信息。 addrlen结构体的大小 返回值说明成功返回0失败返回-1并将错误码进行设置 5. recvfrom – 从套接字中获取信息 int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen); sockfd套接字描述符 buf接受数据的数据缓冲区指针用于接受数据的 len数据缓冲区的大小 flags一般设置为0表示默认的接收行为也可以设置一些标志位来影响接收操作的行为例如 MSG_PEEK查看数据但不实际读取数据仍留在接收队列中、MSG_WAITALL尽可能等待接收完整的请求数据量等但这些标志位的使用相对较少 from是一个struct sockaddr*类型的结构体指针它用来获取发送方的地址信息 fromlen是一个指向整数的指针用于指定 from 所指向的地址结构的长度。在调用 recvfrom 之前应该将 *fromlen 设置为 sizeof(struct sockaddr)或对应的具体地址结构的大小当 recvfrom 返回时fromlen 会被修改为实际存储在 from 中的地址结构的长度 返回值说明成功返回收到的字节数返回值为0说明对端已经关闭了连接返回-1表示接受数据发生了错误 6. sendto – 向套接字中写入信息 int sendto(int sockfd, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen); sockfd是通过socket函数创建的套接字描述符 buf要向套接字发送的数据所在的缓冲区指针 len指定要发生内容的长度 flags: 一般设置为 0表示默认的发送行为。也可以设置一些标志位来影响发送操作的行为不过这些标志位的使用相对较少 to要发送到的目的地的struct sockadd的指针 tolento的大小 7. 网络字节序接口 #include arpa/inet.huint32_t htonl(uint32_t hostlong);//将源主机中32位长整形转换成网络字节序要求的大端格式 uint16_t htons(uint16_t hostshort);//将源主机中16位短整形转换成网络字节序要求的大端格式 uint32_t ntohl(uint32_t netlong);//将网络中32位长整形大端转换成当前主机的格式 uint16_t ntohs(uint16_t netshort);//将网络中16位短整形大端转换成当前主机的格式 8. listen 监听 int listen(int sockfd, int backlog); 它用于将一个套接字标记为被动套接字也就是将其设置为用于监听连接请求的状态。这个套接字通常是由socket函数创建并经过bind函数绑定到一个本地地址和端口之后使用。 sockfd是由socket函数创建并经过bind绑定后的套接字描述符。backlog指定了在拒绝连接之前操作系统可以暂存的未完成连接请求的最大数量。它实际上定义了连接请求队列的长度。当有多个客户端同时尝试连接服务器时这些连接请求会被放入这个队列中等待服务器处理。如果队列已满新的连接请求可能会被拒绝具体行为可能因操作系统而异。返回值如果listen函数调用成功则返回 0如果失败则返回 -1并设置相应的错误代码可以通过perror等函数查看错误信息 9. accept 连接 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 它用于从处于监听状态的套接字的连接请求队列中取出一个连接请求并创建一个新的套接字来与客户端进行通信。这个新的套接字与原来监听的套接字不同它是专门用于和客户端进行数据交互的。 sockfd仍然是最初由socket函数创建并经过bind和listen操作的监听套接字描述符。addr是一个指向sockaddr结构体的指针用于存储客户端的地址信息。如果不需要获取客户端地址信息可以将其设置为NULL。addrlen是一个指向socklen_t类型变量的指针它用于在函数调用前传入addr所指向的结构体的长度在函数返回时它会被设置为实际存储客户端地址信息所占用的长度。返回值如果accept函数调用成功则返回一个新的套接字描述符这个描述符用于与客户端进行后续的通信如果失败则返回 -1并设置相应的错误代码可以通过perror等函数查看错误信息。 四、日志 往往在项目工程中我们会有需要不同的调试信息以及可能一些需要标记或者记录的信息例如用户连接成功的信息用户的消息记录等等这些我们之前都是直接打印在屏幕上的但实际在工程项目中我们都需要将这些信息打印到文件中进行管理这里我们简单实现一下应该粗糙的日志。
- 可变参数 在写日志代码前还需要对可变参数有一些了解像C语言中的printf的实现就是用可变参数的方式实现那么我们要如何使用呢 #include cstdarg // 头文件 void func(const char *format,…) // 可变参数前至少要有一个确定的参数format va_list p; // p变量本质是一个char*的指针 va_start(p, format); // p指向可变参数部分的起始地址 int a va_arg(p, int); // 根据类型提取参数 va_end(p); // p NULL; 以上是对于可变参数的预备知识我们要用可变参数的是为了让打印日志信息函数中可以让外部传入的日志信息可以是多样的像printf一样使用所以可以用到一个函数帮助我们 int vsnprintf(char *str, size_t size, const char *format, va_list ap); str是数据缓冲区 size是缓冲区大小 format是一个格式化字符串用于指定输出的格式其格式规则与printf函数中的格式化字符串类似包含普通字符和格式转换说明符如%d、%f、%s等 ap是一个va_list类型的变量用于传递可变参数列表。它通常是由va_start宏初始化后得到的 2. 参考代码 #pragma once#include iostream #include string #include cstdio #include cstring #include ctime #include cstdarg #include sys/types.h #include unistd.h// 日志是有日志等级的const std::string filename log/tcpserver.log;enum {Debug 0,Info,Warning,Error,Fatal,Uknown };static std::string toLevelString(int level) {switch (level){case Debug:return Debug;case Info:return Info;case Warning:return Warning;case Error:return Error;case Fatal:return Fatal;default:return Uknown;} }static std::string getTime() {time_t curr time(nullptr);struct tm *tmp localtime(curr);char buffer[128];snprintf(buffer, sizeof(buffer), %d-%d-%d %d:%d:%d, tmp-tm_year 1900, tmp-tm_mon1, tmp-tm_mday,tmp-tm_hour, tmp-tm_min, tmp-tm_sec);return buffer; }// 日志格式: 日志等级 时间 pid 消息体 // logMessage(DEBUG, hello: %d, %s, 12, s.c_str()); // DEBUG hello:12, world void logMessage(int level, const char *format, …) {char logLeft[1024];std::string level_string toLevelString(level);std::string curr_time getTime();snprintf(logLeft, sizeof(logLeft), [%s] [%s] [%d] , level_string.c_str(), curr_time.c_str(), getpid());char logRight[1024];va_list p;va_start(p, format);vsnprintf(logRight, sizeof(logRight), format, p);va_end(p);// 打印// printf(%s%s\n, logLeft, logRight);// 保存到文件中FILE *fp fopen(filename.c_str(), a);if(fp nullptr)return;fprintf(fp,%s%s\n, logLeft, logRight);fflush(fp); //可写也可以不写fclose(fp);// 预备// va_list p; // char *// int a va_arg(p, int); // 根据类型提取参数// va_start(p, format); //p指向可变参数部分的起始地址// va_end(p); // p NULL; } 五、守护进程
- 概念 要理解守护进程我们先需要知道一些关于Liunx系统的概念。 首先要理解关于进程、进程组、会话这三个概念 进程是基础 进程是计算机系统中正在运行的程序的实例是操作系统进行资源分配和调度的基本单位。它拥有自己独立的内存空间和系统资源有自己的生命周期。进程组是相关进程的集合 进程组由一个共同的祖先进程创建的多个相关进程组成。这些进程在某些行为上具有一致性比如共享一个进程组 ID对某些信号的处理方式相同。一个进程可以属于一个进程组。会话是包含多个进程组的逻辑概念 会话通常与一个控制终端相关联当用户登录系统时创建。一个会话可以包含多个不同类型的进程组这些进程组中的进程对于某些信号的处理方式是相关的。会话支持作业控制操作。总体关系 一个进程可以属于一个进程组一个进程组可以属于一个会话。从范围上看会话包含多个进程组进程组包含多个相关进程。 我们现在使用的Xshell去登录远端的Linux服务器每次登录其实是在整个Linux系统上启动了一个会话而bash进程作为首个登入的进程同时也是对应进程组的组长 而守护进程就是我们要将原先我们在该会话中写的进程将其独立出去作为一个单独的会话层面的进程让它在后台运行一般的服务器都是如此这样就不会因为我们会话关闭而服务器挂掉。 守护进程要实现还有几个条件 与控制终端脱离 守护进程需要与任何控制终端脱离关系。这是因为如果守护进程依赖于控制终端当终端关闭时例如用户退出登录守护进程可能会收到一些信号如 SIGHUP导致其异常终止。通常通过调用setsid函数来创建一个新的会话使守护进程成为新会话的首进程从而脱离原来的控制终端。改变工作目录 守护进程一般会改变其工作目录到根目录/或者其他合适的目录。这是为了避免因为工作目录所在的文件系统被卸载等原因导致守护进程出现问题。例如如果守护进程的工作目录在一个用户可卸载的文件系统上当该文件系统被卸载时守护进程可能无法正常访问其工作目录中的文件。重设文件描述符 守护进程需要关闭所有不需要的文件描述符。在进程创建时会继承父进程的一些文件描述符如果不关闭这些不必要的文件描述符可能会导致资源浪费以及潜在的问题。例如守护进程可能会因为某个打开的文件描述符指向一个已经不存在的文件如终端相关的文件描述符而出现错误。通常会关闭标准输入、标准输出和标准辅助输入输出等文件描述符0、1、2并可以根据需要重新打开一些文件描述符用于日志记录等用途。设置合适的文件权限掩码 守护进程通常会设置合适的文件权限掩码umask。文件权限掩码决定了新创建文件的默认权限。通过设置合适的文件权限掩码可以确保守护进程创建的文件具有合适的权限避免出现安全问题或文件无法正常使用的情况。忽略某些信号 守护进程需要忽略一些信号比如 SIGHUP 信号。因为守护进程已经与控制终端脱离如果不忽略 SIGHUP 信号当终端关闭时守护进程可能会收到该信号并错误地终止。同时守护进程也可能需要忽略其他一些不相关的信号以确保其稳定运行。 2. 参考代码 #pragma once // 1. setsid(); // 2. setsid(), 调用进程不能是组长我们怎么保证自己不是组长呢 // 3. 守护进程a. 忽略异常信号 b. 0,1,2要做特殊处理 c. 进程的工作路径可能要更改 /#include cstdlib #include unistd.h #include signal.h #include sys/types.h #include sys/stat.h #include fcntl.h#include log.hpp #include err.hpp//守护进程的本质是孤儿进程的一种 void Daemon() {// 1. 忽略信号signal(SIGPIPE, SIG_IGN);signal(SIGCHLD, SIG_IGN);// 2. 让自己不要成为组长if (fork() 0)exit(0);// 3. 新建会话自己成为会话的话首进程pid_t ret setsid();if ((int)ret -1){logMessage(Fatal, deamon error, code: %d, string: %s, errno, strerror(errno));exit(SETSID_ERR);}// 4. 可选可以更改守护进程的工作路径// chdir(/)// 5. 处理后续的对于0,1,2的问题int fd open(/dev/null, O_RDWR);if (fd 0){logMessage(Fatal, open error, code: %d, string: %s, errno, strerror(errno));exit(OPEN_ERR);}dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd); }总结 本篇文章主要是整理了Socket编程常见的接口还有对tcp服务器相关的概念知识点
- 上一篇: 如何自己建立一个网站企业用的邮箱是什么邮箱
- 下一篇: 如何自己建网站做网站放视频
相关文章
-
如何自己建立一个网站企业用的邮箱是什么邮箱
如何自己建立一个网站企业用的邮箱是什么邮箱
- 技术栈
- 2026年03月21日
-
如何自己搭建网站重庆企业网站推广策略
如何自己搭建网站重庆企业网站推广策略
- 技术栈
- 2026年03月21日
-
如何自己创造网站河南公司网站建设
如何自己创造网站河南公司网站建设
- 技术栈
- 2026年03月21日
-
如何自己建网站做网站放视频
如何自己建网站做网站放视频
- 技术栈
- 2026年03月21日
-
如何自己设置网站微信公众平台和微网站的区别
如何自己设置网站微信公众平台和微网站的区别
- 技术栈
- 2026年03月21日
-
如何自己制作一个网站门户网站建设情况
如何自己制作一个网站门户网站建设情况
- 技术栈
- 2026年03月21日






