上次写了篇使用WebBorwser封装网页的博客,收到了不少宝贵意见,后来又懒得去改进,所以也没有新的文章。最近,由于学校宿舍也装校园网了,不再是用Dr.Com,我满怀心喜地试用了Srun3000,结果发现会出现连接上就一两秒掉网现象,并且提示说我的网络出现问题,与服务器连接断开,怒了!我试过各种办法,当然不包括换系统(我用的Win7),还是不行,网卡驱动也都是正常的。于是我试着用Fiddler抓了次HTTP包,发现了问题所在。
原来,srun3000会在发送了连接的包后,又自动发了个注销的包,所以导致我网络中断,并且它在提示错误后就马上自动退出了,导致我产生了程序产生致命错误的假象……好吧,先上个图,看下srun3000的“罪证”:
先后发的两条数据,第一条便是登录,第二条便是注销(为什么会自动注销,我用ollydbg跟进看了一会,由于汇编太难看了,而且我又比较懒,所以没分析出来),这导致了我网络的中断。为了验证我的想法,我使用Fiddler手动把登录的包重发了一次,果然能正常上网了,而且到第二天都没有掉过。所以大家可以看到我这篇文章,同样使用客户端的方式,基于HTTP协议来开发一个代替Srun3000的软件。
1、功能:连接、注销、程序开机启动、自动登录、记住密码、连接后自动最小化
2、界面:WPF窗口、通知栏图标、软件功能设置、异步连接断开
3、效果:
4、基本原理:
从抓到的数据来看,连接和断开都使用的是POST,只需要根据它的格式依照Post数据过去就可以实现连接和断开。
连接:
POST http://172.30.16.53/cgi-bin/srun_portal HTTP/1.1 Content-Type: application/x-www-form-urlencoded User-Agent: my session Host: 172.30.16.53 Content-Length: 138 Pragma: no-cache action=login&username=******&password=******&drop=0&type=2&n=23&ip=0&mbytes=0&minutes=0&ac_id=3&mac=**:**:**:**:**:**&nas_ip=172.30.12.244
断开:
POST http://172.30.16.53/cgi-bin/srun_portal HTTP/1.1 Content-Type: application/x-www-form-urlencoded User-Agent: my session Host: 172.30.16.53 Content-Length: 87 Pragma: no-cache action=logout&ac_id=3&username=******&mac=**:**:**:**:**:**&type=2&nas_ip=172.30.12.244
为了安全起见,我把上面的username、password、mac三个字段的值都用“*”号代替了。
5、核心代码
现在我在C#客户端只需要分别使用HttpWebRequest仿照进行请求就行了,我贴上连接的核心代码:
1 private Func<string> ConnectDelegate = Connect; // 连接委托 2 // 连接执行函数 3 private string Connect() 4 { 5 try 6 { 7 var settings = Properties.Settings.Default; 8 Uri uri = new Uri("http://" + settings.GateIP + settings.RelativeUri); 9 // 新建请求 10 var request = HttpWebRequest.Create(uri); 11 request.Method = "POST"; 12 request.ContentType = "application/x-www-form-urlencoded"; 13 // 请求体 14 var body = Encoding.ASCII.GetBytes( 15 "action=login&username=" + UserName + 16 "&password=" + Password + 17 "&drop=0&type=2&n=23&ip=0&mbytes=0&minutes=0&ac_id=3&mac=" + _MacAddress + 18 "&nas_ip=" + settings.NasIP 19 ); 20 request.ContentLength = body.Length; 21 request.Timeout = 5000; 22 var requestStream = request.GetRequestStream(); 23 requestStream.Write(body, 0, body.Length); 24 requestStream.Close(); 25 // 发送请求并接收结果 26 string responseBody; 27 var response = request.GetResponse(); 28 var responseStream = response.GetResponseStream(); 29 using (StreamReader reader = new StreamReader(responseStream)) 30 responseBody = reader.ReadToEnd(); 31 if (responseBody.Contains("login_ok")) 32 { 33 _State = ConnectionState.Connected; 34 return "连接成功!"; 35 } 36 else if (responseBody.Contains("online_num_error")) 37 { 38 //var ping = new System.Net.NetworkInformation.Ping(); 39 //ping.Send("www.baidu.com"); 40 _State = ConnectionState.Connected; 41 return "已经连接上,请不要重复登录!"; 42 } 43 else if (responseBody.Contains("username_error")) 44 return "帐号错误,请重新输入!"; 45 else if (responseBody.Contains("password_error")) 46 return "密码错误,请重新输入!"; 47 else 48 return "连接失败:" + responseBody; 49 } 50 catch (HttpListenerException ex) 51 { 52 return "HTTP请求出错,请检查网络是否正常!错误代码:" + ex.ErrorCode; 53 } 54 catch (Exception ex) 55 { 56 return "连接失败!错误详情:" + ex.Message; 57 } 58 }
基本情况就是通过POST请求获取返回体,然后根据返回体的内容判断连接状态,这些返回数据牲也是使用Fiddler抓取的,比如“login_ok”、“online_num_error”、“username_error”、“password_error”、“logout_ok”等。当然这仅仅只是数据通信的部分,还有其它与界面相关的处理就不多说了,另外断开操作与连接操作也非常相似,也就不贴了。经过我的观察,由于Srun3000并没有在线检测机制,它只是通知认证网关把相应的MAC地址放行,而且认证服务器也不管回收离线地址(需要Srun3000自己去POST logout请求),所以我也不用去ping了,也不用写心跳包了,so easy。
接下来,我顺便贴上修改注册表使程序开机启动的代码:
1 private void AutoStartupChanged() 2 { 3 RegistryKey HKLM = Registry.LocalMachine; 4 // 确认当前状态 5 string name = AppDomain.CurrentDomain.FriendlyName; 6 name = name.Substring(0, name.LastIndexOf('.')); 7 var run = HKLM.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"); 8 bool hasValue = run.GetValue(name) != null; 9 try 10 { 11 if (!hasValue && AutoStartup) // 添加启动 12 { 13 run = HKLM.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true); 14 run.SetValue(name, AppDomain.CurrentDomain.BaseDirectory + name + ".exe"); 15 } 16 else if (hasValue && !AutoStartup) // 删除启动 17 { 18 run = HKLM.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true); 19 run.DeleteValue(name); 20 } 21 } 22 catch (System.Exception ex) 23 { 24 Message = "操作失败:" + ex.Message; 25 } 26 }
主要也就是使用了SOFTWARE\Microsoft\Windows\CurrentVersion\Run下面的项来增加删除开机启动,这里不采用Windows服务来写的原因是,我想让这个软件成为“绿色”可携带的版本。
其它也就不多说了,此软件目前为止用了一个星期,效果不错,没掉过线也。
转载请注明原址:http://www.cnblogs.com/lekko/archive/2013/01/02/2841840.html