引言
目前使用远程客户端上的Putty,对Linux服务器重启关机等操作,操作过程较繁琐,而遇到n台服务器的情况,操作任务*n,实在不方便,现在就试着写一个程序,可以多台服务器的一键关机等操作。
一、原理
简单点,服务器运行一个服务进程,监听客户端,当有操作命令后,执行相应的命令。二者使用TCP/IP协议通讯。
二、服务器端源码
1.Tcp/IP服务
网上直接找了一段,直接拷贝过来:
//-------tcp相关头文件------
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> //close()
static int socket_fd = 0;
static int socket_c_fd = 0;
//tcp_server_init()
#if 0
1、创建socket
2、设置本地ip和端口以及协议类型
3、绑定
4、监听
5、等待客户端连接
6、收发数据
7、关闭连接
#endif
int tcp_server_init(int port)
{
int ret;
//1
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd == -1)
{
printf("create socket fail\n");
return -1;
}
//2
struct sockaddr_in local_addr,c_addr;
local_addr.sin_family = AF_INET;//IPv4协议
local_addr.sin_port = htons(port);//服务器端口号
local_addr.sin_addr.s_addr = INADDR_ANY;//设置服务器ip
//3
ret = bind(socket_fd, &local_addr, sizeof(local_addr));
if(ret == -1)
{
printf("bind fail\n");
return -1;
}
//4
ret = listen(socket_fd, 3);
if(ret == -1)
{
printf("listen fail\n");
return -1;
}
//5
socklen_t addrlen = 0;
while(1)
{
socket_c_fd = accept(socket_fd, &c_addr, &addrlen);
if(addrlen != 0)
break;
}
printf("client connect\n");
}
//tcp_server_send()
int tcp_server_send(char *buff, int len)
{
write(socket_c_fd, buff, len);
}
//tcp_server_rcv()
int tcp_server_rcv(char *buff, int len)
{
int ret;
ret = read(socket_c_fd, buff, len);
return ret;
}
//tcp_server_close()
int tcp_server_close()
{
close(socket_fd);
}
2.main文件
代码稍微修改以下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "tcp_server.h"
int main()
{
int rcv_len;
char buff[1024] = {0};
tcp_server_init(9090);
while(1)
{
rcv_len = tcp_server_rcv(buff, 1024);
if(rcv_len == 15)
{
printf("tcp rcv: %s\n", buff);
tcp_server_send("server rcv ok\n", 14);
sleep(1);
system(buff);
break;
}
else
{
printf("There is no command from client, waiting!\n");
sleep(1);
}
}
return 0;
}
三、客户端源码
客户端使用QT编写。
1.Tcp/IP客户端
源码修改以下:
#include "clientshut.h"
ClientShut::ClientShut()
{
m_socket = new QTcpSocket();
}
ClientShut::~ClientShut()
{
delete this->m_socket;
}
int ClientShut::Tcp_socket_connect(QString IP)
{
int port = 9090;
// IP = "target2";
//取消已有的连接
m_socket->abort();
//连接服务器
m_socket->connectToHost(IP, port);
//等待连接成功
if(!m_socket->waitForConnected(3000))
{
qDebug() << "Connection failed!";
m_socket->disconnect();
return -1;
}
qDebug() << "Connect successfully!";
return 0;
}
void ClientShut::Tcp_socket_disconnect(){
m_socket->disconnectFromHost();
}
int ClientShut::Tcp_socket_Send(char* buff){
int txlen = m_socket->write(buff);
m_socket->flush();
qDebug() << "Send the Command!";
return txlen;
}
int ClientShut::Tcp_socket_Receive(){
QByteArray ba;
//读取缓冲区数据
ba = m_socket->readAll();
qDebug() << "Receive from the server:";
qDebug() << QString(ba.data());
if(QString(ba.data()) == "server rcv ok")
{
// qDebug() << "Receive from Sever";
m_socket->disconnect();
return 0;
}
else
return -1;
}
调试的时候发现客户端发送命令后,服务器端并没有反应,需要等到客户端程序关闭。找找资料,才知道原来write并不会直接把数据发送出去,而要等到缓冲满或其他条件,所以这里加了一个flush,可以即时发送。
2.main
#include <QCoreApplication>
#include "clientshut.h"
//#include <iostream>
#include <windows.h>
#include <QTextStream>
//using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ClientShut *Me = new ClientShut();
QTextStream cin(stdin, QIODevice::ReadOnly);
QTextStream cout(stdout, QIODevice::WriteOnly);
QTextStream cerr(stderr, QIODevice::WriteOnly);
QString Remcmd = "";
QString Servers="";
cout<<"Enter shutdown command NO.:"<<Qt::endl;
while(1)
{
cout<<"1.shutdown -h now"<<Qt::endl;
cout<<"2.shutdown -H now"<<Qt::endl;
cout<<"3.shutdown -r now"<<Qt::endl;
QString cmdNo = cin.readLine();
if(cmdNo.isValidUtf16())
{ int tmpNo = cmdNo.toInt();
if (tmpNo==1){
Remcmd = "shutdown -h now";
break;
}
else if(tmpNo==2){
Remcmd = "shutdown -H now";
break;
}
else if(tmpNo==3){
Remcmd = "shutdown -r now";
break;
}
else
printf("Enter the right NO. please:\n");
}
else
printf("Select and Enter the cmomand NO. please:\n");
}
cout<<"Enter the Servers name:"<<Qt::endl;
Servers = cin.readLine();
QStringList serverList = Servers.split(QRegExp("\\s+"));
// for(int i = 0; i<serverList.length();i++)
// cout<<serverList[i]<<Qt::endl;
// QString Server = "192.168.2.162";
printf("Start...\n");
// cout<<"Start...\n"<<Qt::endl;
for(int i = 0; i<serverList.length();i++)
{
QString Server = serverList[i];
int Connect = Me->Tcp_socket_connect(Server);
if(!Connect)
{
QByteArray ba= Remcmd.toLatin1();
// char* cmd= "shutdown";
char* cmd;
cmd = ba.data();
int lenTx = Me->Tcp_socket_Send(cmd);
if(lenTx > 0)
qDebug() << "Send sucessfully!";
else
qDebug() << "Send failed!";
cout<<"shutdown " + Server + " now!"<<Qt::endl;
}
else
cout<<"cann't connect " + Server + " !"<<Qt::endl;
Me->Tcp_socket_disconnect();
}
delete Me;
printf("Complete!\n");
// cout<<"Complete!\n";
return a.exec();
// return 0;
}
四、配置服务自启动
服务器端的服务程序应随系统自启动,这里使用service服务实现。
写一个rem-servershut.service文件:
[Unit]
Description=Remote target shutdowN server
After=network.target
[Service]
Type=simple
ExecStart=/home/xxx/RemShutdown/main_tcp_server
User=root
Restart=always
[Install]
WantedBy=multi-user.target
将文件放在/etc/systemd/system/中,使用
systemctl enable rem-servershut.service
命令用于设置开机自启服务。
使用
systemctl start rem-servershut.service
可直接启动相应的服务程序。
运行结果
运行结果如下,多台服务器轻松关闭:
后续
后续把客户端的服务程序打包成安装文件,客户端试试多线程。