简介
- 练习C#套接字(socket)通信的时候,突然产生一个想法,制作一款软件可以远程关闭他人的电脑,于是便着手写了这个程序。
环境需求
- 开发:C#
- 运行环境:.net 4.8及以上
原理分析
- 我们使用socket的时候,客户端和服务器可以进行通讯,想要达到控制他人电脑的效果,必然需要通过网络进行通讯,在这里我将控制端称作服务端,被控制端称作客户端。服务端配置好IP地址和端口(port),开始监听;客户端同样配置好IP和端口(port),启动客户端,客户端根据IP和端口对服务器进行通讯,建立连接,服务器收到通讯请求,返回一个反馈,同时记录下该客户端的IP和端口信息。此时,我们可以在服务器端可以查到哪些设备已经跟我们建立了连接。
- 关闭其实原理很简单,通过客户端调用客户机的命令提示符程序,输入关机指令即可。服务器向客户端发起关机指令,客户端收到指令进行执行,服务器检测到客户端连接中断后,从在线列表中将客户端删除。
运行效果
- 服务端启动
- 客户端连接
- 客户端离线
开始
- 服务端
为了操作方便,我们创建一个窗口程序,将工程命名为controlserver。
绘制好操作界面
由于会使用到配置文件,所以我们新建一个common.cs进行配置文件读取功能的编码
using System.Text;
using System.Runtime.InteropServices;
namespace ControlServer
{
class common
{
[DllImport("kernel32")]//调用DLL
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
/*读取配置文件ini*/
public static string readini(string group, string key, string default_value, string filepath)
{
StringBuilder temp = new StringBuilder();
GetPrivateProfileString(group, key, default_value, temp, 255, filepath);
return temp.ToString();
}
}
}
定义三个静态变量,分别用于socket,记录连接和线程
static Socket socketwatch = null;
static Dictionary<string, Socket> clientConnectionItems = new Dictionary<string, Socket> { };
static Thread myThread = null;
取消textbox的跨线程检测
public server()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;//取消检测
}
监听部分
private void startlisten()
{
/*读取配置文件中的ip与端口*/
string IpValue = common.readini("basic","ip","",".\\config.ini");
string PortValue = common.readini("basic", "port", "", ".\\config.ini");
socketwatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress iP = IPAddress.Parse(IpValue);
int port = int.Parse(PortValue);
IPEndPoint iPEnd = new IPEndPoint(iP, port);
/*绑定节点并开始监听*/
socketwatch.Bind(iPEnd);
socketwatch.Listen(30);
Thread threadlisten = new Thread(ListenConnection);
threadlisten.IsBackground = true;
threadlisten.Start();
myThread = threadlisten;
Logs.Text += "监听开始!\r\n";
Logs.Text += $"监听地址:{IpValue}:{PortValue}\r\n";
this.statues.Text = "Listening...";
this.statues.ForeColor = Color.LightGreen;
}
/*监听*/
private void ListenConnection()
{
Socket connection = null;
while (true)
{
try
{
connection = socketwatch.Accept();
}
catch (Exception ex)
{
Logs.Text += ex.Message + "\r\n";
break;
}
IPAddress ClientiP = (connection.RemoteEndPoint as IPEndPoint).Address;
int ClientPort = (connection.RemoteEndPoint as IPEndPoint).Port;
string sendmsg = $"OK";
byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendmsg);
connection.Send(arrSendMsg);
string remoteEndPoint = connection.RemoteEndPoint.ToString();
Logs.Text += $"成功与客户端 {remoteEndPoint} 建立连接!\r\n";
clientConnectionItems.Add(remoteEndPoint, connection);
Logs.Text += $"当前总连接数:{clientConnectionItems.Count()}\r\n";
IPEndPoint netpoint = connection.RemoteEndPoint as IPEndPoint;
ParameterizedThreadStart pts = new ParameterizedThreadStart(recv);
Thread thread = new Thread(pts);
thread.IsBackground = true;
thread.Start(connection);
RefreshList();
}
}
/*接受消息*/
private void recv(object socketclientpara)
{
Socket socketServer = socketclientpara as Socket;
while (true)
{
try
{
byte[] arrServerRecMsg = new byte[1024 * 1024];
int length = socketServer.Receive(arrServerRecMsg);
string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length);
if (strSRecMsg == "1")
{
socketServer.Send(Encoding.UTF8.GetBytes("OK"));
}
}
catch (Exception ex)
{
Logs.Text += $"客户端{socketServer.RemoteEndPoint.ToString()}连接中断" + ex.Message + "\r\n";
clientConnectionItems.Remove(socketServer.RemoteEndPoint.ToString());
Logs.Text += $"当前连接数:{clientConnectionItems.Count()}\r\n";
socketServer.Close();
RefreshList();
break;
}
}
}
接下来就是服务端一些界面上的显示与操作代码,大致参考一下即可
private string GetTime()//获取时间
{
DateTime currentTime = new DateTime();
currentTime = DateTime.Now;
return currentTime.ToString("yyyy-MM-dd HH:mm:ss");
}
private void LoadClientList()//装载list
{
int i = 0;
foreach (var EndPoint in clientConnectionItems)
{
string[] ipPort = EndPoint.Key.ToString().Split(':');
this.ClientList.Items.Add(ipPort[0]);
this.ClientList.Items[i].SubItems.Add(ipPort[1]);
this.ClientList.Items[i].SubItems.Add("YES");
this.ClientList.Items[i].SubItems.Add(GetTime());
this.ClientList.Items[i].SubItems.Add("None");
i++;
}
}
private void RefreshList()//刷新list
{
ClientList.Items.Clear();
LoadClientList();
}
private void StartListenButton_Click(object sender, EventArgs e)//启动按钮
{
startlisten();
RefreshList();
}
private void ClientList_SelectedIndexChanged(object sender, EventArgs e)
{
this.SeletedIp.Text = this.ClientList.FocusedItem.SubItems[0].Text;
this.SeletedPort.Text = this.ClientList.FocusedItem.SubItems[1].Text;
}
private void TurnOffButton_Click(object sender, EventArgs e)//关闭按钮
{
string ipvalue = this.SeletedIp.Text + ":" +this.SeletedPort.Text;
try
{
clientConnectionItems[ipvalue].Send(Encoding.UTF8.GetBytes("0"));
}
catch
{
MessageBox.Show("找不到对应的客户端!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
- 客户端
由于客户端不需要显示出来,我们可以创建一个console程序,当然,也可以创建窗口程序
首先我们重写一下程序可见性
protected override void SetVisibleCore(bool a)
{
base.SetVisibleCore(false);
}
收发消息
private void GetConnection()
{
this.Hide();
string IpValue = common.readini("basic", "ip", "", ".\\config.ini");
string PortValue = common.readini("basic", "port", "", ".\\config.ini");
socketclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(IpValue);
int port = int.Parse(PortValue);
IPEndPoint iPEnd = new IPEndPoint(address, port);
try
{
socketclient.Connect(iPEnd);
}
catch (Exception)
{
}
threadclient = new Thread(recv);
threadclient.IsBackground = true;
threadclient.Start();
}
private void recv()
{
int x = 0;
while (true)
{
try
{
byte[] arrRecvmsg = new byte[1024 * 1024];
int length = socketclient.Receive(arrRecvmsg);
string strRevMsg = Encoding.UTF8.GetString(arrRecvmsg, 0, length);
if (x == 1)
{
if (strRevMsg == "OK")
{
WaitForTime();
}
else
{
ShutdownWindows();
}
}
else
{
x = 1;
}
}
catch (Exception)
{
//MessageBox.Show(ex.Message, "error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void sendmsg(string sendMsg)
{
byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg);
socketclient.Send(arrClientSendMsg);
}
关机指令
private void ShutdownWindows()
{
string str = "shutdown -s -t 0";
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动
p.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
p.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
p.StartInfo.CreateNoWindow = true;//不显示程序窗口
p.Start();//启动程序
p.StandardInput.WriteLine(str + "&exit");
p.StandardInput.AutoFlush = true;
p.WaitForExit();
p.Close();
}
结语
- 本程序主要难点在于客户机与服务器通讯保持和信息交换,处理好这两点,可以依次类推,以此为基础编写各种C/S模式程序
如果我的文章对你有帮助的话,不妨打赏我一下哦~
最后一次更新于2019-09-18
@
对你有用就好
By AnsoNeko at September 19th, 2019 at 01:18 pm.