一个简单的 socket 小工具
作者:为自己带盐
- 2021 年 12 月 18 日
本文字数:2839 字
阅读完需:约 9 分钟
这个也是搬运的之前博客的内容,感觉沉淀总结的还不够,近期真的太忙了。。
背景
记得彼时彼刻,我们有一个需求,就是要不定期的把一些本地资源拷贝到附件服务器,拷贝的方式,就是手工操作。
后来我实在是嫌麻烦,就写了个小工具,在一定程度上提高了工作效率。
思路
当时做这个工具,思路也很简单。
1.在服务器的后台跑一个程序,监听一个 http 端口;
2.每次需要传输文件的时候,客户端先向服务端发送一条消息;
3.服务端收到消息后,会响应客户端,收到可以传输的响应信号后,就可以开始传输文件了。
4.服务端解压缩(可选)
因为我们当时传输的文件是要打包并分片传输的,因为特别零散,不打包直接传的话,时间太长,而打包的话,服务端的程序就需要具备解压缩的能力。
代码
/// <summary>
/// 发送数据包
/// </summary>
/// <param name="s"></param>
/// <param name="data"></param>
/// <returns></returns>
public static int SendVarData(Socket s, byte[] data)
{
int total = 0;
int size = data.Length;
int dataleft = size;
int sent;
byte[] datasize = new byte[4];
try
{
datasize = BitConverter.GetBytes(size);
sent = s.Send(datasize);
while (total < size)
{
sent = s.Send(data, total, dataleft, SocketFlags.None);
total += sent;
dataleft -= sent;
}
return total;
}
catch
{
return 3;
}
}
/// <summary>
/// 接受数据包
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static byte[] ReceiveVarData(Socket s)
{
int total = 0;
int recv;
byte[] datasize = new byte[4];
recv = s.Receive(datasize, 0, 4, SocketFlags.None);
int size = BitConverter.ToInt32(datasize, 0);
int dataleft = size;
byte[] data = new byte[size];
while (total < size)
{
recv = s.Receive(data, total, dataleft, SocketFlags.None);
if (recv == 0)
{
data = null;
break;
}
total += recv;
dataleft -= recv;
}
return data;
}
/// <summary>
/// 发送文件
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static bool SendFile(string IP, int Port, string fullPath)
{
//创建一个文件对象
FileInfo EzoneFile = new FileInfo(fullPath);
//打开文件流
FileStream EzoneStream = EzoneFile.OpenRead();
//包的大小
int PacketSize = 10000;
//包的数量
int PacketCount = (int)(EzoneStream.Length / ((long)PacketSize));
//最后一个包的大小
int LastDataPacket = (int)(EzoneStream.Length - ((long)(PacketSize * PacketCount)));
//指向远程服务端节点
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(IP), Port);
//创建套接字
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接到发送端
try
{
client.Connect(ipep);
Common.ConsoleM("connect...ok!");
}
catch
{
Common.ConsoleM("连接服务器失败!");
return false;
}
//获得客户端节点对象
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
//发送[文件名]到客户端
TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes("F_" + EzoneFile.Name));
//发送[包的大小]到客户端
TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(PacketSize.ToString()));
//发送[包的总数量]到客户端
TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(PacketCount.ToString()));
//发送[最后一个包的大小]到客户端
TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(LastDataPacket.ToString()));
bool isCut = false;
//数据包
byte[] data = new byte[PacketSize];
int cnt = 0;
//开始循环发送数据包
for (int i = 0; i < PacketCount; i++)
{
//从文件流读取数据并填充数据包
EzoneStream.Read(data, 0, data.Length);
//发送数据包
if (TransferFiles.SendVarData(client, data) == 3)
{
isCut = true;
return false;
}
System.Threading.Thread.Sleep(100);
Console.WriteLine(" ");
Console.SetCursorPosition(0, Console.CursorTop - 1);
float percent = i * 100 / PacketCount;
cnt = i;
Console.Write(DateTime.Now + "-已发送" + cnt + "个包,共有" + PacketCount + "个包");
}
//如果还有多余的数据包,则应该发送完毕!
if (LastDataPacket != 0)
{
data = new byte[LastDataPacket];
EzoneStream.Read(data, 0, data.Length);
TransferFiles.SendVarData(client, data);
}
if (cnt < PacketCount)
{
Console.WriteLine("");
Common.ConsoleM("余量包发送完毕,共计发送" + PacketCount + "个数据包");
}
//关闭套接字
client.Close();
//关闭文件流
EzoneStream.Close();
if (!isCut)
{
return true;
}
return false;
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static bool SendMsg(string IP, int Port, string Msg)
{
//指向远程服务端节点
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(IP), Port);
//创建套接字
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接到发送端
try
{
client.Connect(ipep);
//Common.ConsoleM("connect...ok!");
TransferFiles.SendVarData(client, System.Text.Encoding.Unicode.GetBytes(Msg));
while (true)
{
string ret = System.Text.Encoding.Unicode.GetString(TransferFiles.ReceiveVarData(client));
if (!string.IsNullOrEmpty(ret))
{
Common.ConsoleM(ret);
break;
}
}
}
catch
{
Common.ConsoleM("连接服务器失败!");
return false;
}
return true;
}
复制代码
好了大概就是这个样子,至于客户端和服务端交互上,我就不贴代码了,非常简单,而且我写的风格也没啥参考价值,最多就是多了一个思路,大家完全可以自己改造。完整代码在这→https://gitee.com/Tony_df/SocketProject
最后的效果是这样
因为是内部使用,所以就做成了控制台程序,并且自定义了几个命令参数,然后还可以通过批处理文件来快速启动客户端
划线
评论
复制
发布于: 3 小时前阅读数: 18
为自己带盐
关注
学着码代码,学着码人生。 2019.04.11 加入
狂奔的小码农
评论