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 显示硬件具体信息,甚至包括一组八个的流 ...
随机推荐
- Lua-面向对象中类的构造
在Lua中,我们可以通过table+function来模拟实现类. 而要模拟出类,元表(metatable)和__index元方法是必不可少的. 为一个表设置元表的方法: table = {} met ...
- JavaScript数组的reduce方法详解
数组经常用到的方法有push.join.indexOf.slice等等,但是有一个经常被我们忽略的方法:reduce,这个方法简直强大的不要不要的. 我们先来看看这个方法的官方概述:reduce() ...
- iOS 事件处理之UIResponder简介
在用户使用app过程中,会产生各种各样的事件 iOS中的事件可以分为3大类型:触摸事件.加速计事件.远程控制事件 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处 ...
- mvp+retrofit+rxjava
引用 "retrofit" : "com.squareup.retrofit2:retrofit:2.0.1", "retrofit-adapter& ...
- ObjectAnimator.start()工作原理
分析下面一段代码的逻辑 objectAnimator.start(); 他会调用父类的start(),即ValueAnimator,我们分析valueAnimator.start()即可 ValueA ...
- dropzonejs中文翻译手册 DropzoneJS是一个提供文件拖拽上传并且提供图片预览的开源类库.
http://wxb.github.io/dropzonejs.com.zh-CN/dropzonezh-CN/ 由于项目需要,完成一个web的图片拖拽上传,也就顺便学习和了解了一下前端的比较新的技术 ...
- ORACLE Linux以及 Unbreakable Enterprise Kernel
Oracle Linux,全称为Oracle Enterprise Linux,简称OEL,Linux发行版本之一.Oracle公司在2006年初发布第一个版本,以对Oracle软件和硬件支持较好见长 ...
- SQL Server 2008 R2 升级到 Service Pack 3后Report Builder启动不了
一同事将测试服务器从SQL Server 2008 R2 SP2升级到了SQL Server 2008 R2 SP3后发现Report Service的报表编辑时启动不了Report Builder, ...
- 使用强大的可视化工具redislive来监控我们的redis,别让自己死的太惨~~~
作为玩windows的码农,在centos上面装点东西,真的会崩溃的要死,,,我想大家也知道,在centos上面,你下载的是各种源代码,需要自己编译...而 使用yum的话,这个吊软件包有点想nuge ...
- SQL Server高级查询
简介 关于数据库,我们经常会听说"增查删改"之类的词语,听起来很简单,但是如果想要准确的获取到需要的数据的话,还是要花点功夫的.下面由我来和大家谈谈高级查询的用法以及和普通查询的区 ...