前言
前面介绍了与CAD相关的一些功能,本期介绍一下C#和python如何通过socket通信来进行文件的传输,建议不了解Socket的同学先去了解一下socket的通讯原理,这样看起来不会云里雾里的。
一、C#端(服务端)
C#端使用的Socket基于TCP协议,首先我们定义一个Socket对象,记得要添加引用
using System.Net.Sockets;
using System.Net;
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
然后在设置绑定的ip及端口,ip为本机ip,端口设置成你所需要的即可。
serverSocket.Bind(new IPEndPoint(IPAddress.Parse("10.148.88.192"), 8888));
接下来就是设置监听数量
serverSocket.Listen(5);
最后就是等待客户端发送请求过来,当接收到请求后,就会调用send_file()方法,自动打开CAD,并生成一些图块,文字,标注(之前所介绍过的),然后打印成pdf文件,并且将打印出的pdf文件发送给python客户端。
while (true)
{
Socket client = serverSocket.Accept();
string str = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Console.WriteLine("[INFO]:" + str);
send_file(client);
//顯示請求的ip地址
IPAddress adress = ((IPEndPoint)client.RemoteEndPoint).Address;
string ip = adress.ToString();
Console.WriteLine("[INFO]:收到請求!" + ip);
}
下面贴上send_file方法的代码,里面主要包含了打开CAD文档,执行打印pdf功能,将生成好的pdf文件发送给客户端。
private static void send_file(Socket client)
{
object[] obj = openCad();
if (null == obj)
{
Console.WriteLine("[ERROR]:Cad啟動失敗");
}
// 获取App对象和Doc对象
AcadApplication App = (AcadApplication)obj[0];
AcadDocument Doc = (AcadDocument)obj[1];
try
{
// 发送cad命令
Doc.SendCommand("(command " + "\"" + "cad" + "\"" + " (handent " + "\"" + "\")) ");
}
catch (System.Runtime.InteropServices.COMException e)
{
Console.WriteLine("[ERROR]:發送命令到cad時發生異常" + e.Message);
}
if (plotPdf(Doc, new double[] { 0, 0, 0 }, new double[] { 90, 297, 0 }) == false)
{
Console.WriteLine("[ERROR]:打印pdf失敗\n");
}
Doc.SaveAs(Application.StartupPath + "\\" + "123.dwg");
Doc.Close();
App.Quit();
if (File.Exists(@"C:\Users\F1655392.SZYZ\Documents\PDF 檔案\自動儲存\"))
{
byte[] png1 = packFile(Application.StartupPath + "\\" + "source-逃料圖.png", true);
client.Send(png1, SocketFlags.None);
Console.WriteLine("[INFO]:pdf發送完成");
}
if (File.Exists(Application.StartupPath + "\\" + "123.dwg"))
{
byte[] dwg = packFile(Application.StartupPath + "\\" + "123.dwg", true);
client.Send(dwg, SocketFlags.None);
Console.WriteLine("[INFO]:CAD圖檔發送完成");
}
// 最后发送一句话本次发送文件成功
byte[] arr3 = packMessage("本次发送成功", true);
client.Send(arr3, SocketFlags.None);
}
openCad()方法实现打开项目目录下的source.dwg文档,并将AcadApplication对象和AcadDocument对象存到数组中并回传。
private static object[] openCad()
{
AcadApplication App = null;
AcadDocument Doc = null;
try
{
App = (AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject("AutoCad.Application");
}
catch
{
try
{
App = new AcadApplication();
}
catch
{
return null;
}
}
App.Visible = true;
App.Documents.Close();//關閉默認打開的Drawing1.dwg文檔
Doc = App.Documents.Open(Application.StartupPath + "\\" + "source.dwg");
App.ZoomExtents();//使圖檔大小自適應窗口
object[] arr = new object[2];
arr[0] = App;
arr[1] = Doc;
return arr;
}
下面重点来了,在发送文件过程中遇到了一些坑,就是在发送文件的时候会出现粘包现象,客户端在接收完第一个文件之后,接收第二个文件就会不完整,这是因为连续发送两个文件过去,导致在接收过程中客户端不知道什么时候接收完,而且我们项目发送的文件很小只有几百kb而已,也会出现粘包的问题。之前有试过设置暂停时间来隔开发送,但是电脑性能不同,暂停的时间不好控制,而且这样治标不治本。
那么应该如何解决呢?通过度娘的寻找,找到了一个解决方法,在发送文件的时候,把文件的长度和文件一起打包发送给客户端,客户端根据文件大小来接收文件,直到接收完毕为止,这样就不会出现所谓的粘包现象了。
C#端打包文件和打包信息的代码如下:
public static byte[] packFile(string filePath, bool result)
{
byte[] file = File.ReadAllBytes(filePath);
byte[] fileLength = BitConverter.GetBytes(file.Length);
byte[] res = BitConverter.GetBytes(result);
Console.WriteLine("[INFO]:文件大小" + file.Length);
return fileLength.Concat(res).Concat(file).ToArray();
}
public static byte[] packMessage(string message, bool result)
{
byte[] file = Encoding.UTF8.GetBytes(message);
byte[] fileLength = BitConverter.GetBytes(file.Length);
byte[] res = BitConverter.GetBytes(result);
Console.WriteLine("[INFO]:結束信息大小" + file.Length);
return fileLength.Concat(res).Concat(file).ToArray();
}
二、python端(客户端)
python端的socket通讯相对比较简单,这里直接把代码贴上
def gen_dwg(file_name):
sk = socket.socket() # 创建一个socket对象
sk.connect(('10.148.88.192', 8888)) # 连接服务端ip
message = '请生成PDF文件'
sk.send(bytes(message, encoding='utf-8'))
f_head = sk.recv(5)
file_size, result = struct.unpack('i?', f_head)
buff_size = 1024
if result:
with open('C:\\Users\\F1655392.SZYZ\\Desktop\\source.dwg', 'wb') as fp:
rest_size = file_size
while 1:
if rest_size > buff_size:
file_data = sk.recv(buff_size)
else:
file_data = sk.recv(rest_size)
if not file_data:
break
fp.write(file_data)
rest_size = rest_size - len(file_data)
if rest_size == 0:
break
print("[INFO:]pdf生成成功!")
else:
message = sk.recv(file_size)
print(message.decode('utf8'))
这里需要注意的地方就是,我们需要将C#服务端打包发送过来的数据进行解包
file_size, result = struct.unpack('i?', f_head)
file_size就是文件的大小,result就是文件,然后就是不断接收直到接收的字节等于file_size,这样粘包的问题就得到了解决。
写到这里,CAD二次开发的基本功能就介绍到这里了,当然CAD二次开发的功能远不止这些,具体可参考这本书,里面有很多关于CAD二次开发的功能介绍。
AutoCAD VBA&VB.NET开发基础与实例教程(第二版)