前言:
作为一个转专业来软件的苦逼孩子,自然需要好好学习以跟上各位大佬。于是学期之初,便跟随一位老师在实验室学习且干活,以求最大化的学习效率,虽然到了期末发现自己仍然是咸鱼一只。然后寒假考完试放假后因老师需求,和自己的觉醒,决定在实验室多待两个星期,一方面为了预习大二下因转专业需要补课,而其与大二下课程结合而产生的巨多的课程(亚历山大);另一方面是为了学习更多的知识。
项目需求:
老师的研究方向因做神经网络及深度学习算法需要大量数据,根据老师的需求需要大量关于FBX模型文件的最佳视点数据,故老师要求我通过Unity设计一款iOS上的App,通过HolokitSDK来利用AR的特性获取大量数据,并寻找人力主观观察后将数据集收集起来。(听到任务的时候,头都是大的)需要我学习Unity,HolokitSDK(相对简单)以及C#开发等等。
(下面的项目分析比较浅显,望大佬勿喷)
- 目的:设计收集动作的最佳视点数据的App
- 语言:C#
- 工具:Unity,HolokitSDK
- 功能:文件读写,网络数据传输,动态读取模型并播放动画,前端设计
收获及反思
- 文件数据存储
思路
主要是因为iOS的沙盒机制,程序的数据文件彼此间不互通,所以我们不能通过本地文件读写后直接通过文件浏览寻找到存储的数据文件。而在Unity开发中有关于iOS数据文件存储的相关封装,其介绍链接贴在本小节后方,于是选择运行时动态生成数据文件存储在Application.persistentDataPath,方便读写。不过注意该路径不能提前放入文件,且该路径的数据会被自动备份,某种意义上可能会阻挡App的审核。
Unity四大系统读写目录
- SMTP简单邮件传输协议
思路
既然我们已经成功将数据文件存储下来了,所以我们该想一想方法来将数据发送出来。当然因为Application.persistentDataPath路径下的数据可被自动备份,可通过电脑获取,但是需要对手机额外的操作不慎方便,我的计划是,点击一个Button,即将数据文件发送出来。所以我看向了邮件系统,这是我选择用来将iOS中的最佳视点数据文件(以下简称数据文件)发送出来的一个方法。
OK,确定使用SMTP协议了,接下来就是寻找相关类,在C#内已经有封装好的System.Net.Mail。基本思路是使用邮箱以及密码或者授权码创建一个SmtpClient对象,再创建Message对象并设置好Sender,Receiver并添加需要发送的内容以及附件,最后经由SmtpClient发送。然后问题来了!天呐,我能说,SMTP很坑嘛!(很有可能是我还没系统学SMTP,所以对其以及SSL不甚了解,望耐心看完我文章的大佬能悉心教导)
以下是下文天坑之路的最后的问题的参考链接
465端口发送邮件失败原因以及解决办法(懒癌晚期的我没去用,等待微软修复)
天坑出现
SMTP协议呢,经过测试发现,在现在已经基本弃用了无法通过SSL加密邮件的25号端口。虽然说,在MacOS上能够在设置EnableSsl = false并且通过25号端口发送邮件,但是将App导出至iOS上后,发送邮件失败,XCode报错Authentication is required。好嘛好嘛,那我为了能在iOS上发送邮件,用SSL加密可好?
设置EnableSsl = true后,结果MacOS也发不了了!WTF!!没办法只能到处找案例,想方法,结果发现网络上的大佬都没问题,代码移植到Windows上也没问题,倒是StackOverflow上的有一个问题让我去翻了翻MacOS上与Unity配套的编辑器Mono的相关文档,嗯,没证书,需要自己安装,感觉被世界针对了。好嘛好嘛,那我为了能在MacOS上发邮件,安装证书可好?
安装需要测试的邮箱的SSL证书Over,根据网络在MacOS上大佬们的方法,可以EnableSsl = true的时候继续使用25非SSL端口,MacOS发送邮件测试,OK,心里有点小窃喜,马上就能做完了。机械性App导出到iOS上,测试,发送邮件失败,XCode报错The authentication or decryption has failed以及SSL authentication error。好嘛好嘛,那我为了能在iOS上发送邮件,用SSL加密端口可好?
经过MacOS平台测试,各大邮箱465端口还是发不了,而且不抛异常,没用异步发送故按下Send程序就一直卡在发送(也就是说,应该是超时)。而每个大型邮箱似乎都有自己的第二个SSL加密端口,后发现QQ邮箱的587端口邮件在MacOS上发送的很成功,就不管465了。OK,心里又有点小窃喜,App导出到iOS上后,发送邮件失败,XCode报错Need EHLO and AUTH first !大概就是说,发送邮件的步骤顺序有问题?一切回到了原点?
至此,心态奔溃,彻底完蛋,决定抛弃SMTP,不用邮件发送数据。
最后贴上MailManager代码如下
public class MailManager:MonoBehaviour{
public string ReceiverMail;//Confirm At Unity
public int SSLPort;//Confirm At Unity
string SenderMail = "[email protected]";//Your mail account
string SenderPwd = AuthenticationCode;//Your password or authentication code
string smtpSender = "smtp.qq.com";//The responding smtp server
public void sendMail()
{
//Sender Client
SmtpClient mailClient = new SmtpClient (smtpSender , SSLPort);
mailClient.EnableSsl = true;//Confirm the SSL authentication
mailClient.UseDefaultCredentials = false;
mailClient.Credentials = new NetworkCredential(SenderMail, SenderPwd) as ICredentialsByHost;
//Mail
//Mail Receiver
MailMessage message = new MailMessage(new MailAddress(SenderMail), new MailAddress(ReceiverMail));//(sender mailAddress , receiver mailAddress)
//message.Bcc.Add(new MailAddress("[email protected]")); //Can add more receivers via this method
//Mail Content
message.Body = "This is a automatic mail from the FBXPreviewer";
message.Subject = "Automaic Mail";
//Mail Attachment
string viewSightDocPath = Application.persistentDataPath + "/data.txt";
if (File.Exists (viewSightDocPath)) {
Attachment att = new Attachment (viewSightDocPath);
message.Attachments.Add (att);//add Attachment
} else {
message.Body += "\nNo such file";
}
try{
mailClient.Send(message);
}catch(SmtpException ex){
Debug.Log (ex.Message + ":Send failed");
}
}
}
邮箱测试数据
PS.
1.以下测试数据均在Mono已经安装证书的基础上进行测试得到
2.若MacOS发送邮件测试失败,则不进行iOS发送邮件测试
3.最后的994端口是网易163邮箱的第二个SSL端口
SSL EnSSL Platform Pass ExceptionMsg
25 false MacOs true
false iOS false Authentication is required
25 true MacOS true
true iOS false User has no permission:Send failed
465 true MacOS false App dead
587 true MacOS true
true iOS false Need EHLO and AUTH first !
994 true MacOS false App dead
结束SMTP惨无人道的经历回想
3.TCP传输控制协议
思路
OK,在被SMTP虐一遍之后并完全抛弃其后,总得想个办法来解决数据传输问题吧,需要传输出来就得用网络吧,要用网络就需要传输协议吧,这个时候突然想到TCP,简单的发送文本数据可好。哇哈哈哈激动(又要走上被虐之路,大概是受虐症复发)
关键问题是,以前只听过TCP这个名字,也还是像对SMTP一样什么都不了解,想用却啥基础都没有,一筹莫展。所幸的是,C#多少有点良心把TCPClient和TCPListener封装好,可以直接用,而不用通过Socket进行编程,多少降低了对我的虐待。
不过在应用TCP的时候,多少因为同步和异步的问题困扰了很久,在考虑一段时间后,决定放弃多线程同步编程(我不会哇!),采用异步连接。
不过仍然存在问题:我对TCP暂时的学习导致理解不甚,程序仅可用于局域网进行TCP数据传输,而不可用于广域网。希望诸位大佬能给予指点迷津,先跪为敬,orz
模块基本设计
TCPServer:采取同步连接,主线程监听指定端口,设置最大连接数为一。当有连接请求时,接入连接,获取网络流,进行数据传输,并检测Client端的连接情况。
TCPClient:采取异步连接(为了在发送和连接TCPServer的时候不干扰Unity主线程),异步连接Server端,连接上后,获取网络流,并在按下Send时进行同步数据发送(使用App的人必须确认是否发送成功),并在每一个Unity的Update时检测Server端的连接情况,若断开连接,重新发起异步连接请求,即自动重连。
代码介绍
PS.
因此处TCPClient以及TCPListener封装较好,故仅贴较重要的代码
判断远端是否在线(采用Socket类的Poll函数)
bool isOnline(TcpClient tcp){
return !((tcp.Client.Poll (1000, SelectMode.SelectRead) && (tcp.Client.Available == 0)) || !tcp.Client.Connected);
//set 1000us as time delay to detect that if the TcpClient has been offline.
}
Client端自动重连
bool asynOver;//Used for record if the asynchronous connect is over to prevent the func BeginConnect from being called repeatly
void Update(){//This func will be called per frame in Unity
if (isOnline(tcpClient)) {
ConnectionText.text = "TCP:Connected";
isConnected = true;
} else {
ConnectionText.text = "TCP:Disconnected";
isConnected = false;
ConnectRetry ();//If offline , reConnect.
}
}
void ConnectRetry ()
{
if (asynOver) {
tcpClient.Close ();
tcpClient = new TcpClient ();//When retry connection, it seems that we need construct a new TcpClient
tcpClient.BeginConnect (IPAddress.Parse (RemoteIP), TCPPort, new AsyncCallback (Connected), tcpClient);//When the func is over , call Connected func.
asynOver = false;//Start to asynConnection
}
}
void Connected(IAsyncResult iar){
tcpClient = iar.AsyncState as TcpClient;
tcpClient.EndConnect (iar);//End the tcp asynchronous connect in the separate thread
//Attention:The func will be blocked here until asynchronous connect is over in the separate thread
asynOver = true;//AsynConnection is over
if (tcpClient != null && tcpClient.Connected) {
tcpStream = tcpClient.GetStream ();
}
}
终章
其实还是有很多想总结的,但无奈不知从何说起,故此时仅总结以上三个方面。有关于文件流,Unity,HolokitSDK的使用,以后有机会再说。如果看官们有收获,不妨点个赞,或者给个关注。
另外吐个槽:MarkDown编辑器的排版功能,对缩进的支持好像不是很高,我觉得我写的总结太挤了,不是很美观,如果有大佬愿意和我分享下关于排版的心得,我将万分感激。
再啰嗦一下:如果对我的代码,或者我的方法有改进的建议的话,请跟我联系,或者在下方评论。