• 你碰到过StreamReader.Peek的异常情况


    工作中有个需求需要采集每个服每天用户的登录信息、道具使用情况等(用来做数据分析),这些信息、数据

    是通过技术那边的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上面那简短的解释,不知哪位

    大佬曾经碰到过这问题!



    扫描上面二维码关注我
    如果你真心觉得文章写得不错,而且对你有所帮助,那就不妨帮忙“推荐"一下,您的“推荐”和”打赏“将是我最大的写作动力!
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.
  • 相关阅读:
    JavaEE Tutorials (25)
    洛谷 P2677 超级书架 2
    洛谷 P1029 最大公约数和最小公倍数问题
    洛谷 P1305 新二叉树
    洛谷 P3817 小A的糖果
    洛谷 P1618 三连击(升级版)
    洛谷 P2097 资料分发1
    洛谷 P1068 分数线划定
    洛谷 P1207 [USACO1.2]双重回文数 Dual Palindromes
    洛谷 P1223 排队接水
  • 原文地址:https://www.cnblogs.com/kerrycode/p/1916141.html
Copyright © 2020-2023  润新知