现在的位置: 首页 > 综合 > 正文

替换Srun3000解决掉网问题

2012年12月21日 ⁄ 综合 ⁄ 共 4142字 ⁄ 字号 评论关闭

  上次写了篇使用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

抱歉!评论已关闭.