Linux Linux程序练习十八
- 题目:编写一个TCP服务器和客户端,基于TCP的并发远程shell
- 要求实现:
- )对于所有收到的客户端消息,作为命令行进行执行,
- 并且将命令行的输出结果返回给客户端
- )要求使用并发结构
- )实现关键代码
- 子进程执行命令
- numbytes = read(connfd, buf, );
- buf[numbytes] = '\0';
- sprintf(cmd, "%s > /tmp/cmd.txt", buf);
- system(cmd);
- fp = fopen("/tmp/cmd.txt", "r");
- numbytes = fread(cmd, , *, fp);
- cmd[numbytes] = '\0';
- fclose(fp);
- 核心代码展示
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/select.h>
- #include <fcntl.h>
- #include <termios.h>
- #include <signal.h>
- #include "commsock.h"
- #define MAXBUFSIZE 1020
- //报文结构
- typedef struct _packet
- {
- int len;
- char buf[MAXBUFSIZE];
- } Packet;
- /**
- * readn - 读取固定大小的字节
- * @fd:文件描述符
- * @buf:接收缓冲区
- * @count:指定读取字节数
- * 成功返回count,失败返回-1,对等方连接关闭返回<count
- * */
- int readn(int fd, void *buf, int count)
- {
- int nread = ;
- int lread = count;
- char *pbuf = (char *) buf;
- while (lread > )
- {
- do
- {
- nread = read(fd, pbuf, lread);
- } while (nread == - && errno == EINTR);
- if (nread == -)
- return -;
- else if (nread == )
- return count - lread;
- lread -= nread;
- pbuf += nread;
- }
- return count;
- }
- /**
- * writen - 写固定大小字节数
- * @fd:文件描述符
- * @buf:写入缓冲区
- * @count:指定写入字节数
- * 成功返回count,失败返回-1
- * */
- int writen(int fd, void *buf, int count)
- {
- int lwrite = count;
- int nwrite = ;
- char *pbuf = (char *) buf;
- while (lwrite > )
- {
- do
- {
- nwrite = write(fd, pbuf, lwrite);
- } while (nwrite == - && errno == EINTR);
- if (nwrite == -)
- return -;
- lwrite -= nwrite;
- pbuf += nwrite;
- }
- return count;
- }
- /**
- * read_timeout - 读超时检测函数,不含读操作
- * @fd:文件描述符
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回-1,超时返回-1并且errno=ETIMEDOUT
- * */
- int read_timeout(int fd, unsigned int wait_seconds)
- {
- int ret = ;
- if (wait_seconds > )
- {
- fd_set readfds;
- FD_ZERO(&readfds);
- FD_SET(fd, &readfds);
- struct timeval timeout;
- timeout.tv_sec = wait_seconds;
- timeout.tv_usec = ;
- do
- {
- ret = select(fd + , &readfds, NULL, NULL, &timeout);
- } while (ret == - && errno == EINTR);
- //ret==-1
- if (ret == )
- {
- errno = ETIMEDOUT;
- ret = -;
- } else if (ret == )
- {
- ret = ;
- }
- }
- return ret;
- }
- /**
- * write_timeout - 写超时检测函数,不含写操作
- * @fd:文件描述符
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回-1,超时返回-1并且errno=ETIMEDOUT
- * */
- int write_timeout(int fd, unsigned int wait_seconds)
- {
- int ret = ;
- if (wait_seconds > )
- {
- fd_set writefds;
- FD_ZERO(&writefds);
- FD_SET(fd, &writefds);
- struct timeval timeout;
- timeout.tv_sec = wait_seconds;
- timeout.tv_usec = ;
- do
- {
- ret = select(fd + , NULL, &writefds, NULL, &timeout);
- } while (ret == - && errno == EINTR);
- //ret==-1
- if (ret == )
- {
- errno = ETIMEDOUT;
- ret = -;
- } else if (ret == )
- {
- ret = ;
- }
- }
- return ret;
- }
- /**
- * activate_nonblock - 设置套接字非阻塞
- * @fd:文件描述符
- * 成功返回0,失败返回-1
- * */
- int activate_nonblock(int fd)
- {
- int ret = ;
- int flags = fcntl(fd, F_GETFL);
- if (flags == -)
- return -;
- flags = flags | O_NONBLOCK;
- ret = fcntl(fd, F_SETFL, flags);
- //ret==-1
- return ret;
- }
- /**
- * deactivate_nonblock - 设置套接字阻塞
- * @fd:文件描述符
- * 成功返回0,失败返回-1
- * */
- int deactivate_nonblock(int fd)
- {
- int ret = ;
- int flags = fcntl(fd, F_GETFL);
- if (flags == -)
- return -;
- flags = flags & (~O_NONBLOCK);
- ret = fcntl(fd, F_SETFL, flags);
- return ret;
- }
- /**
- * connect_timeout - 带超时的connect(函数内已执行connect)
- * @fd:文件描述符
- * @addr:服务器网络地址结构
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回-1,超时返回-1并且errno=ETIMEDOUT
- * */
- int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
- {
- int ret = ;
- if (wait_seconds > )
- {
- if (activate_nonblock(fd) == -)
- return -;
- }
- ret = connect(fd, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
- if (ret == - && errno == EINPROGRESS)
- {
- fd_set writefds;
- FD_ZERO(&writefds);
- FD_SET(fd, &writefds);
- struct timeval timeout;
- timeout.tv_sec = wait_seconds;
- timeout.tv_usec = ;
- int nwrite = select(fd + , NULL, &writefds, NULL, &timeout);
- //nwrite==-1 此时ret==-1
- if (nwrite == )
- errno = ETIMEDOUT;
- else if (nwrite == )
- {
- int err = ;
- socklen_t len = sizeof(err);
- ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
- if (ret == )
- {
- if (err != )
- {
- errno = err;
- ret = -;
- }
- }
- }
- }
- if (wait_seconds > )
- {
- if (deactivate_nonblock(fd) == -)
- return -;
- }
- return ret;
- }
- /**
- * sock_init - 初始化SOCKET环境
- * @connid:连接套接字
- * 成功返回0,失败返回错误码
- * */
- int sock_init(int *connid)
- {
- int ret = ;
- if (connid == NULL)
- {
- ret = SckParamErr;
- printf("cltsock_init() params not correct !\n");
- return ret;
- }
- //init
- ret = socket(AF_INET, SOCK_STREAM, );
- if (ret == -)
- {
- ret = SckBaseErr;
- perror("socket() err");
- return ret;
- } else
- {
- *connid = ret;
- ret = ;
- }
- return ret;
- }
- /**
- * connect_server - 连接服务器
- * @connid:连接套接字
- * @port:端口号
- * @ipaddr:IP地址
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回错误码
- * */
- int connect_server(int connid, int port, char *ipaddr,
- unsigned int wait_seconds)
- {
- int ret = ;
- if (connid < || port < || port > || ipaddr == NULL
- || wait_seconds < )
- {
- ret = SckParamErr;
- printf("cltsock_init() params not correct !\n");
- return ret;
- }
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons();
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- ret = connect_timeout(connid, &addr, wait_seconds);
- if (ret == -)
- {
- if (errno == ETIMEDOUT)
- {
- ret = SckTimeOut;
- printf("connect_timeout() time out !\n");
- return ret;
- }
- ret = SckBaseErr;
- perror("connect_timeout() err");
- return ret;
- }
- return ret;
- }
- /**
- * send_packet - 发送数据包
- * @fd:文件描述符
- * @pack:数据包
- * @buflen:数据包大小
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回错误码
- * */
- int send_packet(int fd, Packet *pack, int buflen, unsigned int wait_seconds)
- {
- int ret = ;
- //可写检测
- ret = write_timeout(fd, wait_seconds);
- if (ret == -)
- {
- if (errno == ETIMEDOUT)
- {
- ret = SckTimeOut;
- printf("write_timeout() time out !\n");
- return ret;
- }
- ret = SckBaseErr;
- perror("write_timeout() err");
- return ret;
- }
- //发送数据
- ret = writen(fd, pack, buflen);
- if (ret != buflen)
- {
- ret = SckBaseErr;
- perror("writen() err");
- return ret;
- }else
- {
- ret=;
- }
- return ret;
- }
- /**
- * send_packet - 接收数据包
- * @fd:文件描述符
- * @pack:数据包
- * @buflen:数据包大小
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回错误码
- * */
- int recv_packet(int fd, Packet *pack, int *buflen, unsigned int wait_seconds)
- {
- int ret = ;
- //读超时检测
- ret = read_timeout(fd, wait_seconds);
- if (ret == -)
- {
- if (errno == ETIMEDOUT)
- {
- ret = SckTimeOut;
- printf("read_timeout() time out !\n");
- return ret;
- }
- ret = SckBaseErr;
- perror("read_timeout() err");
- return ret;
- }
- //获取数据长度
- int len = ;
- ret = readn(fd, &pack->len, );
- if (ret == -)
- {
- ret = SckBaseErr;
- perror("readn() err");
- return ret;
- } else if (ret < )
- {
- ret = SckPeerClosed;
- printf("peer is closed !\n");
- return ret;
- }
- //网络字节序转化成本地字节序
- len = ntohl(pack->len);
- //获取包体
- ret = readn(fd, pack->buf, len);
- if (ret == -)
- {
- ret = SckBaseErr;
- perror("readn() err");
- return ret;
- } else if (ret < len)
- {
- ret = SckPeerClosed;
- printf("peer is closed !\n");
- return ret;
- } else if (ret == len)
- ret = ;
- *buflen = len;
- return ret;
- }
- /**
- * product_ser - 处理服务器消息
- * @fd:文件描述符
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 成功返回0,失败返回错误码
- * */
- int product_ser(int fd, unsigned int wait_seconds)
- {
- int ret = ;
- Packet pack;
- int buflen = ;
- while ()
- {
- memset(&pack, , sizeof(pack));
- ret = recv_packet(fd, &pack, &buflen, wait_seconds);
- if (ret != )
- {
- return ret;
- }
- //已经完全接收服务器所有数据
- if (buflen == && strncmp(pack.buf, "end", ) == )
- {
- break;
- }
- //printf("数据包长度是%d;%s\n",buflen,pack.buf);
- fputs(pack.buf, stdout);
- }
- return ret;
- }
- /**
- * run_clt - 运行客户端
- * @connid:连接套接字
- * @wait_seconds:等待超时秒数,如果为0表示不检测超时
- * 失败返回错误码
- * */
- int run_clt(int connid, unsigned int wait_seconds)
- {
- int ret = ;
- //安装信号
- if (signal(SIGPIPE, handler) == SIG_ERR)
- {
- ret = SckBaseErr;
- printf("signal() failed !\n");
- return ret;
- }
- Packet pack;
- memset(&pack, , sizeof(pack));
- int buflen = ;
- write(STDIN_FILENO,"请输入shell命令:",sizeof("请输入shell命令:"));
- while (fgets(pack.buf, MAXBUFSIZE, stdin) != NULL)
- {
- //去除\n
- buflen = strlen(pack.buf) - ;
- pack.len = htonl(buflen);
- //发送数据
- ret = send_packet(connid, &pack, buflen + , wait_seconds);
- if (ret != )
- {
- return ret;
- }
- memset(&pack, , sizeof(pack));
- //接收服务器数据
- ret = product_ser(connid, wait_seconds);
- if (ret != )
- return ret;
- write(STDIN_FILENO,"请输入shell命令:",sizeof("请输入shell命令:"));
- }
- return ret;
- }
- /**
- * close_socket - 关闭连接
- * @fd:文件描述符
- * 成功返回0
- * */
- int close_socket(int fd)
- {
- int ret = ;
- close(fd);
- return ret;
- }
- /*
- * clear_back - 退格键不回显
- * 成功返回0,失败返回错误码
- * */
- int clear_back()
- {
- int ret = ;
- struct termios term;
- memset(&term, , sizeof(term));
- //获取当前系统设置
- if (tcgetattr(STDIN_FILENO, &term) == -)
- {
- ret = SckBaseErr;
- perror("tcgetattr() err");
- return ret;
- }
- //修改系统设置
- term.c_cc[VERASE] = '\b';
- //立即生效
- if (tcsetattr(STDIN_FILENO, TCSANOW, &term) == -)
- {
- ret = SckBaseErr;
- perror("tcsetattr() err");
- return ret;
- }
- return ret;
- }
- /**
- * listen_socket - 创建服务器监听套接字
- * @fd:套接字
- * @port:端口号
- * 成功返回0,失败返回错误码
- * */
- int listen_socket(int fd, int port)
- {
- int ret = ;
- if (port < || port < || port > )
- {
- ret = SckParamErr;
- printf("listen_socket() params not correct !\n");
- return ret;
- }
- //reuse addr
- int optval = ;
- ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
- if (ret == -)
- {
- ret = SckBaseErr;
- perror("setsockopt() err");
- return ret;
- }
- //bind
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
- if (ret == -)
- {
- ret = SckBaseErr;
- perror("bind() err");
- return ret;
- }
- //listen
- ret = listen(fd, SOMAXCONN);
- if (ret == -)
- {
- ret = SckBaseErr;
- perror("listen() err");
- return ret;
- }
- return ret;
- }
- /**
- * product_clt - 处理客户端信息
- * @fd:文件描述符
- * 成功返回0,失败返回错误码
- * */
- int product_clt(int fd)
- {
- int ret = ;
- //接收客户端信息
- Packet pack;
- memset(&pack, , sizeof(pack));
- int buflen = ;
- ret = recv_packet(fd, &pack, &buflen, );
- if (ret != )
- return ret;
- //重新拼接shell脚本
- char *path = "/home/test/1/cmdres.txt";
- char tempbuf[] = { };
- if (buflen > )
- {
- ret = SckBaseErr;
- printf("用户输入数据过长,服务器无法接收!\n");
- return ret;
- }
- sprintf(tempbuf, "%s > %s", pack.buf, path);
- //执行shell脚本
- system(tempbuf);
- //打开文件
- FILE *pfr = NULL;
- pfr = fopen(path, "r");
- if (pfr == NULL)
- {
- ret = SckBaseErr;
- perror("fopen() err");
- return ret;
- }
- memset(&pack, , sizeof(pack));
- //读文件
- while (fgets(pack.buf, MAXBUFSIZE, pfr) != NULL)
- {
- //每读取一次,发送一个数据包
- buflen = strlen(pack.buf);
- pack.len = htonl(buflen);
- ret = send_packet(fd, &pack, buflen + , );
- if (ret != )
- {
- printf("发送数据失败!");
- break;
- }
- memset(&pack, , sizeof(pack));
- }
- //关闭文件流
- if (pfr != NULL)
- {
- fclose(pfr);
- pfr = NULL;
- }
- //文件读取完毕之后,发送一个0数据包,告诉客户端数据已经发送完毕
- memset(&pack, , sizeof(pack));
- strcpy(pack.buf, "end");
- buflen = strlen(pack.buf);
- pack.len = htonl(buflen);
- ret = send_packet(fd, &pack, buflen + , );
- if(ret!=)
- {
- return ret;
- }
- //删除临时文件
- system("rm ../1/*");
- return ret;
- }
- /**
- * handler - 信号捕捉函数
- * @sign:信号值
- * */
- void handler(int sign)
- {
- if (sign == SIGPIPE)
- {
- printf("accept SIGPIPE!\n");
- }
- }
- /**
- * select_socket - select机制管理客户端连接
- * @fd:文件描述符
- * 失败返回错误码
- * */
- int select_socket(int fd)
- {
- int ret = ;
- //安装信号
- if (signal(SIGPIPE, handler) == SIG_ERR)
- {
- ret = SckBaseErr;
- printf("signal() failed !\n");
- return ret;
- }
- //定义客户端套接字临时变量
- int conn = ;
- struct sockaddr_in peeraddr;
- socklen_t peerlen = ;
- //已经处理的select事件
- int nread = ;
- //创建客户端连接池
- int cltpool[FD_SETSIZE] = { };
- //初始化连接池
- int i = ;
- for (i = ; i < FD_SETSIZE; i++)
- {
- cltpool[i] = -;
- }
- //定义数组尾部元素下标
- int maxindex = ;
- //定义最大的套接字(初始值是监听套接字)
- int maxfd = fd;
- //定义最新的套接字集合
- fd_set allsets;
- FD_ZERO(&allsets);
- //定义需要监听的套接字集合
- fd_set readfds;
- FD_ZERO(&readfds);
- //将监听套接字加入最新的套接字集合
- FD_SET(fd, &allsets);
- while ()
- {
- //将最新的套接字集合赋值给需要监听的套接字集合
- readfds = allsets;
- do
- {
- nread = select(maxfd + , &readfds, NULL, NULL, NULL);
- } while (nread == - && errno == EINTR); //屏蔽信号
- if (nread == -)
- {
- ret = SckBaseErr;
- perror("select() err");
- return ret;
- }
- //1.服务器监听套接字处理
- if (FD_ISSET(fd, &readfds))
- {
- //接收到客户端的连接
- memset(&peeraddr, , sizeof(peeraddr));
- peerlen = sizeof(peeraddr);
- conn = accept(fd, (struct sockaddr *) &peeraddr, &peerlen);
- if (conn == -)
- {
- ret = SckBaseErr;
- perror("accept() err");
- return ret;
- }
- //将客户端连接添加到连接池
- for (i = ; i < FD_SETSIZE; i++)
- {
- if (cltpool[i] == -)
- {
- if (i > maxindex)
- {
- maxindex = i;
- }
- cltpool[i] = conn;
- break;
- }
- }
- if (i == FD_SETSIZE)
- {
- ret = SckBaseErr;
- close(conn);
- printf("客户端连接池已满!\n");
- return ret;
- }
- if (conn > maxfd)
- maxfd = conn;
- //将该客户端套接字加入到最新套接字集合
- FD_SET(conn, &allsets);
- printf("server accept from :%s\n",inet_ntoa(peeraddr.sin_addr));
- if (--nread <= )
- continue;
- }
- //处理客户端请求
- if (nread <= )
- continue;
- for (i = ; i <= maxindex; i++)
- {
- if (cltpool[i] == -)
- continue;
- if (FD_ISSET(cltpool[i], &readfds))
- {
- //处理客户端请求
- ret = product_clt(cltpool[i]);
- if (ret != )
- {
- //从最新的套接字集合中删除
- FD_CLR(cltpool[i], &allsets);
- //处理请求失败,关闭客户端连接
- close(cltpool[i]);
- //从客户端连接池中清除
- cltpool[i] = -;
- break;
- }
- if (--nread <= )
- break;
- }
- }
- }
- return ret;
- }
Linux Linux程序练习十八的更多相关文章
- 漫谈程序员(十八)windows中的命令subst
漫谈程序员(十八)windows中的命令subst 用法格式 一.subst [盘符] [路径] 将指定的路径替代盘符,该路径将作为驱动器使用 二.subst /d 解除替代 三.不加任何参数键入 ...
- Linux系列教程(十八)——Linux文件系统管理之文件系统常用命令
通过前面两篇博客,我们介绍了Linux系统的权限管理.Linux权限管理之ACL权限 介绍了通过设定 ACL 权限,我们为某个用户指定某个文件的特定权限,这在Linux只能对于一个文件只能有所有者权限 ...
- Linux学习之CentOS(十八)-----恢复Ext3下被删除的文件与 使用grep恢复被删文件内容(转)
前言 下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件. 删除文件 假设我们有一个文件名叫 'test.txt' $ls -il test.txt 15 -rw-rw-r– 2 roo ...
- Linux经常使用命令(十八) - find概述
Linux下find命令在文件夹结构中搜索文件,并运行指定的操作.Linux下find命令提供了相当多的查找条件,功能非常强大.由于find具有强大的功能,所以它的选项也非常多.当中大部分选项都值得我 ...
- Linux 入门记录:十八、Linux 系统启动流程 + 单用户修改 root 密码 + GRUB 加密
一.系统启动流程 一般来说,Linux 系统的启动流程是这样的: 1. 开机之后,位于计算机主板 ROM 芯片上的 BIOS 被最先读取,在进行硬件和内存的校验以及 CPU 的自检没有异常后, BIO ...
- Linux学习总结(十八)几个简单的文本处理工具cut sort tr split
1 命令cut 用来截取某一个字段格式 : cut -d '分隔符' [-cf] n, n为数字,表示第几段-d:后面跟分隔符,分割符要加单引号-c:后面接的是第几个字符-f:后面接的是第几段那么意思 ...
- 学习笔记:CentOS7学习之十八:Linux系统启动原理及故障排除
目录 学习笔记:CentOS7学习之十八:Linux系统启动原理及故障排除 18.1 centos6系统启动过程及相关配置文件 18.1.1 centos6系统启动过程 18.1.2 centos6启 ...
- 鸟哥的linux私房菜——第十六章学习(程序管理与 SELinux 初探)
第十六章.程序管理与 SE Linux 初探 在 Linux 系统当中:"触发任何一个事件时,系统都会将他定义成为一个程序,并且给予这个程序一个 ID ,称为 PID,同时依据启发这个程序的 ...
- 最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示。 HardInfo 显示硬件具体信息,甚至包括一组八个的流行的性能基准程序,你可以用它们评估你的系统性能。 KInfoCenter 和 Lshw 也能够显示硬件的详细信息,并且可以从许多软件仓库中获取。
最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示. HardInfo 显示硬件具体信息,甚至包括一组八个的流 ...
随机推荐
- php实现设计模式之 访问者模式
<?php /** * 访问者模式 * 封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. * 行为类模式 */ /** 抽象访问者:抽象类或 ...
- socket.io,命名空间
原文:http://www.cnblogs.com/xiezhengcai/p/3966263.html 命名空间 在api部分我们说io.connect('ws://103.31.201.154:5 ...
- 8、ASP.NET MVC入门到精通——View(视图)
本系列目录:ASP.NET MVC4入门到精通系列目录汇总 View视图职责是向用户提供界面.负责根据提供的模型数据,生成准备提供给用户的格式界面. 支持多种视图引擎(Razor和ASPX视图引擎是官 ...
- 【再探backbone04】单页应用的基石-路由处理
前言 首先发一点牢骚,互联网公司变化就是快,我去一个团队往往就一年时间该团队就得解散,这不,公司居然把无线团队解散了,我只能说,我那个去!!! 我去年还到处让人来呢,一个兴兴向荣的团队说没就没了啊!我 ...
- browserify压缩合并源码反编译
最近在学习钉钉(一个协作应用)桌面应用的前端源码时候,发现其js源码是用browserify做模块开发.于是想还原其源码的原本的目录结构,学习它的目录分类以及业务划分. 前言 用过browserify ...
- Sql 获取日期区间
获取制定日期区间 declare @d as date declare @d2 as date set @d = '2014-06-03' set @d2 ='2014-06-10' ),datead ...
- DVWA安装,ALMP环境搭建以及php版本转换
前言 本文记录DVWA(Damn Vulberability Web App)在虚拟机中安装配置,包括ALMP环境的搭建和php版本的转换. 目录 2. ALMP环境搭建 3. php版本切换 一. ...
- iOS之加密的三种方法
//需要导入 #import <CommonCrypto/CommonCryptor.h> ==============MD5加密============ NSString *str ...
- 【代码笔记】iOS-获取系统完成任务所需的后台时间
一,代码. AppDelegate.h #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplica ...
- 敏捷开发与jira之流程
敏捷流程在Jira中的运用