你碰到过StreamReader.Peek的异常情况
2010-12-25 11:52 潇湘隐者 阅读(2489) 评论(6) 编辑 收藏 举报工作中有个需求需要采集每个服每天用户的登录信息、道具使用情况等(用来做数据分析),这些信息、数据
是通过技术那边的Http接口来获取,为了提高效率,节省流量,没有用XML或JSON格式的输出(数据量比较
大,用XML格式或JSON格式或增加许多冗余的信息,优劣暂且不讨论),而是用简单的文本形式,Http接口
数据的输出的格式如下:
1:不同记录以"\r\n"分隔
2:不同字段以“|||”分隔
举个例子(假设),用户每天的登录信息:
字段排列顺序为: GameID|||UserID|||UserName|||ServerID|||ServerName|||IP|||RegistedFrom
|||FromSiteUrl|||LoginTimes|||CreateDate
下面是其中一个函数,大家如果不想看,完全可以先跳过,只是为了说明下面的问题
public bool ParseDataFromUrl(string url, string serverName, string dateTime) { HttpWebResponse httpResponse = RequestUtil.GetHttpResponse(url); if (httpResponse == null) { log.Error(url + ": 解析URL内容出错,请检查配置表"); SendMessage.MessageSending(serverName + ": 解析URL内容出错,请检查配置表"); return false; } StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream(), Encoding.GetEncoding("UTF-8")); string[] columns = { "ID", "GameID", "UserID", "UserName", "ServerID", "ServerName", "IP", "RegistedFrom", "FromSiteUrl", "LoginTimes", "CreateDate", "GatherDate" }; Type[] dataTypes = { typeof(Int32), typeof(Int32), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Int32), typeof(Int32), typeof(string) }; DataTable dtDataSource = DataTableUtil.DataTableStruct(columns, dataTypes); try { string line = ""; string tableName = ConfigurationSettings.AppSettings["UserLoginInfoTable"].ToString().Trim(); //while ((line = streamReader.ReadLine()) != null) while (streamReader.Peek() >= 0) { line = streamReader.ReadLine(); if (line == "<?php exit;?>" || line == "") continue; if (!line.Contains("|||")) { log.Fatal("ParseDataFromUrl: " + serverName + url + "----没有权限获取日志内容 "); SendMessage.MessageSending(serverName + "无权限访问日志内容,请检查配置表"); continue; } string[] logText = line.Split(new string[] { "|||" }, StringSplitOptions.None); DataRow row = dtDataSource.NewRow(); row["ID"] = 0; if (RegexUtil.IsNumeric(logText[2])) { row["GameID"] = Int32.Parse(logText[2]); } else { row["GameID"] = 0; } row["ServerID"] = logText[3].Trim(); row["ServerName"] = serverName; row["IP"] = logText[7].Trim(); row["UserID"] = logText[0].Trim(); row["UserName"] = logText[1].Trim(); row["RegistedFrom"] = logText[5].Trim(); row["FromSiteUrl"] = logText[4].Trim(); if (RegexUtil.IsNumeric(logText[8].Trim())) { row["LoginTimes"] = Int32.Parse(logText[8]); } else { row["LoginTimes"] = 0; } if (RegexUtil.IsNumeric(logText[9])) { row["CreateDate"] = Int32.Parse(logText[9]); } else { row["CreateDate"] = 0; } row["GatherDate"] = dateTime; dtDataSource.Rows.Add(row); if (dtDataSource.Rows.Count == 5000) { ImportToDataBase(dtDataSource, tableName); dtDataSource.Rows.Clear(); } } httpResponse.Close(); streamReader.Close(); if (dtDataSource.Rows.Count > 0 && dtDataSource.Rows.Count < 5000) { ImportToDataBase(dtDataSource, tableName); } } catch (Exception exc) { log.Fatal("函数ParseDataFromUrl出错:" + exc.Message); SendMessage.MessageSending("函数ParseDataFromUrl出错, 详情请见日志!"); } return true; } public static HttpWebResponse GetHttpResponse(string url) { try { HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url); httpRequest.Timeout = 6000000; //1000 * 60 * 10; 用后面的效率低下,会有多余的计算步骤 httpRequest.ContentType = "application/x-www-form-urlencoded"; httpRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; ccdotnet ;.NET CLR 1.1.4322; .NET CLR 2.0.50727)"; httpRequest.Accept = "*/*"; httpRequest.KeepAlive = true; httpRequest.Headers.Add("Accept-Language", "zh-cn, en-us; q=0.5"); httpRequest.AllowAutoRedirect = true; HttpWebResponse response = (HttpWebResponse)httpRequest.GetResponse(); return response; } catch (Exception exc) { return null; } }
在测试过程中,遇到了一个让我比较郁闷的问题:采集下来的数据跟接口提供的数据条数不一致,调
试过程居然发现每次运行取得的数据居然都不一致,刚开始还以为是HttpWebResponse
httpResponse = RequestUtil.GetHttpResponse(url);里面HttpWebRequest对象的Timeout
设置过短的超时引起的数据异常,等到把这个Timeout设置很长后,定位错误情况的时候,居然发现
罪魁祸首是:while (streamReader.Peek() >= 0),往往还没有获取所有记录,Peek就返回了-1
, 查了下MSDN,关于Peek的结束如下:
Peek 方法返回一个整数值以便确定是否到达文件末尾,或发生其他错误。这样一来,用户在将返回值
强制转换为 Char 类型之前就可以首先检查该值是否为 -1。换句话说,它不需要先转换字符,即可返
回是否达到文件末尾。
http://msdn.microsoft.com/zh-cn/library/system.io.streamreader.peek.aspx。
现在情况是StreamReader对象还没读写到文件末尾,那肯定是发生了其它错误,但是又没有什么异
常抛出,我只好改用while ((line = streamReader.ReadLine()) != null)来读写,测试结果完全
正确,于是网上搜索了下,发现邀月的博客的博客里面有涉及这方面的内容
http://www.cnblogs.com/downmoon/archive/2010/08/18/1802095.html
,不过他里面所涉及的是StringReader.ReadLine出现的异常的情况(具体情况大家可以去他博客
看看):
StringReader.ReadLine 方法将行定义为后面跟有下列符号的字符序列:换行符(“\n”)、回车符
(“\r”)或后跟换行符的回车符(“\r\n”)。 所产生的字符串不包含终止回车符和/或换行符。 如果
已到达基础字符串的结尾,则返回值为 null 。我的理解:如果由于编码的问题,导致读取异常,也
就是无法读取行标志时,可能会认为已到文件结尾而中断下行的读取。这也解释了为什么会有时读取
不完整的原因。
搜索了很久发现了有一些网友也碰到了类似的问题,不过也没发现有价值的解释,我列举下搜索到的
一些资料
http://www.netframeworkdev.com/net-framework-networking-communication
/streamreader--peek-null-61609.shtml
http://bbs.bccn.net/viewthread.php?tid=88673
http://www.pcreview.co.uk/forums/thread-1333549.php
http://www.go4answers.com/Example/streamreaderpeek-blocks-theres-no-172470.aspx
这些人碰到的问题大体跟我一样,不过都没啥合理的原理解释,除了MSDN上面那简短的解释,不知哪位
大佬曾经碰到过这问题!