红俊's profile蝈蝈俊的共享空间PhotosBlogListsMore ![]() | Help |
|
July 30 对 ActiveMQ .NET 1.1.0 组件接受中文信息乱码的修复最近在使用 Apache ActiveMQ 消息队列做一些工作,在使用的时候,发现发送消息的内容是中文时,获得消息时,获得的是乱码。 发送和接收我使用的是 ActiveMQ .NET 1.1.0 组件。 分析原因进去后,发现是这个开源组件的 Apache.NMS.ActiveMQ 组件的 \Apache.NMS.ActiveMQ-1.1.0-src\src\main\csharp\Transport\Stomp\StompWireFormat.cs 文件对应的 StompWireFormat 类的 public Object Unmarshal(BinaryReader dis) 方法中有错误。 错误在该文件的 166 行 之前的错误代码如下: public Object Unmarshal(BinaryReader dis) { string command; do { command = ReadLine(dis); } while (command == ""); Tracer.Debug("<<<< command: " + command); IDictionary headers = new Hashtable(); string line; while ((line = ReadLine(dis)) != "") { int idx = line.IndexOf(':'); if (idx > 0) { string key = line.Substring(0, idx); string value = line.Substring(idx + 1); headers[key] = value; Tracer.Debug("<<<< header: " + key + " = " + value); } else { // lets ignore this bad header! } } byte[] content = null; string length = ToString(headers["content-length"]); if (length != null) { int size = Int32.Parse(length); content = dis.ReadBytes(size); // Read the terminating NULL byte for this frame. int nullByte = dis.Read(); if(nullByte != 0) { Tracer.Debug("<<<< error reading frame null byte."); } } else { MemoryStream ms = new MemoryStream(); int nextChar; while((nextChar = dis.Read()) != 0) { if( nextChar < 0 ) { // EOF ?? break; } ms.WriteByte((byte)nextChar); } content = ms.ToArray(); } Object answer = CreateCommand(command, headers, content); Tracer.Debug("<<<< received: " + answer); return answer; } internal String ReadLine(BinaryReader dis) { MemoryStream ms = new MemoryStream(); while (true) { int nextChar = dis.Read(); if (nextChar < 0) { throw new IOException("Peer closed the stream."); } if( nextChar == 10 ) { break; } ms.WriteByte((byte)nextChar); } byte[] data = ms.ToArray(); return encoding.GetString(data, 0, data.Length); } 这个源代码文件可以在下面地址看到: 正确的代码如下: internal string ReadLine(NetworkStream ns) { MemoryStream ms = new MemoryStream(); while (true) { int nextChar = ns.ReadByte(); if (nextChar < 0) { throw new IOException("Peer closed the stream."); } if (nextChar == 10) { break; } ms.WriteByte((byte)nextChar); } byte[] data = ms.ToArray(); return encoding.GetString(data, 0, data.Length); } public Object Unmarshal(BinaryReader dis) { NetworkStream ns = dis.BaseStream as NetworkStream; if (ns == null) return null; if (!ns.CanRead) return null; // 读取 command 信息 string command; do { command = ReadLine(ns); } while (command == ""); Tracer.Debug("<<<< command: " + command); // 读取 header 信息 IDictionary headers = new Hashtable(); string line; while ((line = ReadLine(ns)) != "") { int idx = line.IndexOf(':'); if (idx > 0) { string key = line.Substring(0, idx); string value = line.Substring(idx + 1); headers[key] = value; Tracer.Debug("<<<< header: " + key + " = " + value); } else { // lets ignore this bad header! } } // 读取消息内容 MemoryStream ms = new MemoryStream(); do { int t = ns.ReadByte(); if (t <= 0) break; ms.WriteByte((byte)t); } while (ns.DataAvailable); byte[] content = ms.ToArray(); Object answer = CreateCommand(command, headers, content); Tracer.Debug("<<<< received: " + answer); return answer; } 之前的错误代码,在发送“a1郭红俊b2” 这样的中英文数字混合的信息时,发送时,发送的 byte数组 信息如下: 97,49,233,131,173,231,186,162,228,191,138,98,50 接受时,接受到的 byte 数组信息就变成了 97,49,239,191,189,239,191,189,98,50
原先的 BinaryReader dis 其实是个这个开源组件自己写的派生自BinaryReader 的 OpenWireBinaryReader 类。这个类有很多不完善的地方。 这部分的逻辑可以在 TcpTransport 类的下面调用中看到 private readonly Socket socket;
socketReader = new OpenWireBinaryReader(new NetworkStream(socket)); 原先的 OpenWireBinaryReader 不完善的地方: 1、传送中文时,会丢数据;ms.WriteByte((byte)nextChar); 会让本来nextChar对应的 byte 数组,只取了数组的第一项,数组的其他项则丢失了; 2、编码混乱,unicode 和 utf-8 转换有问题。
参考资料: Apache ActiveMQ
ActiveMQ .NET
July 27 Path.Combine("d:\\projects", "\\20090716\\11")); 的计算结果下面代码执行的结果一样么? Console.WriteLine(Path.Combine("d:\\projects", "\\20090716\\11"));
答案是不一样的,执行的结果分别是: \20090716\11
为何会这样呢?? MSDN 中文上的解释如下,红色字体是导致这个问题的根本原因: 如果 path2 不包括根(例如,如果 path2 没有以分隔符或驱动器规格起始),则结果是两个路径的串联,具有介于其间的分隔符。如果 path2 包括根,则返回 path2。 MSDN 英文的解释如下: If path2 does not include a root (for example, if path2 does not start with a separator character or a drive specification), the result is a concatenation of the two paths, with an intervening separator character. If path2 includes a root, path2 is returned. 显然,除了 除了驱动器开头的路径会认为是根, / 或者 \ 开头的也被认为是根,所以才有上面的计算结果 比如: \\myserver\myshare\foo\bar\baz.txt 这个路径中, 我们就可以看到 / 确实有必要作为根路径 在这里 / 或者 \ 被等同处理,是因为各个操作系统上确实用的不一样。如下: Path.DirectorySeparatorChar 字段 Path.AltDirectorySeparatorChar 字段
Path.VolumeSeparatorChar 字段
参考资料: Path.Combine 方法
http://msdn.microsoft.com/en-us/library/fyy7a5kt.aspx
Path.Combine (合并两个路径字符串)方法的一些使用细节
July 16 学习笔记:11种行为型设计模式简单对比这几种行为型设计模式分别为:
对比:
参考: 学习笔记:7种结构型设计模式简单对比 学习笔记:5种创建型设计模式简单对比 Gof 23 中模式关系图 July 15 同名函数的 带 params 参数 与 不带 params 参数的执行优先级看下面代码,编译会不会报错,如果不报错,执行的结果是啥? using System; class Program { static void Main(string[] args) { Console.WriteLine(GetIP()); Console.WriteLine(GetIP("a")); Console.WriteLine(GetIP("a", "b")); Console.WriteLine(GetIP("a", "b","c")); Console.ReadLine(); } public static string GetIP(params string[] ipArr) { return "3"; } public static string GetIP(string ip) { return "2"; } public static string GetIP() { return "1"; } } 答案: 编译不会报错,执行结果: 1
使用ILdasm察看,显然,params 是一个语法糖,编译时,如果发现有完全匹配的函数,就优先调用完全匹配的函数,否则就调用带params 参数的函数。 这个逻辑是编译时确定的,执行时根本不考虑这个问题。 July 03 Connection Pool Timeout 与 Connect Timeout = 0最近在做多线程处理数据库的程序时,这个程序总是会报如下错误: 超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小。 System.Data 在 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) 在 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) 在 System.Data.SqlClient.SqlConnection.Open() 仔细用 SQL Server Management Studio 中的 Activity Monitor 查看数据库链接,竟然是只有2,3个数据库链接时,就报上述错误,很是怪异。 一步步删除掉代码,反复试验后,竟然是数据库链接字符串中的 Connect Timeout=0 来作怪的。 比如下述数据库链接字符串就会出现上述问题, Persist Security Info=False;Integrated Security=SSPI;Initial Catalog=DB1;server=(local);Connect Timeout=0 而把数据库链接字符串修改为 就不会有问题了。
这个bug在微软的反馈中可以看到,如下: 其中微软的官方反馈是: The fix was submitted to the source branch of the next major .Net release.
照这么说,估计.net 4.0 中会修复这个bug。
参考资料: Why Does a Connection Pool Overflow? Trace a SqlConnection - Connection Pooling issues Fixing connection pooling timeout exceptions on third-party code Connection Pooling and the "Timeout expired" exception FAQ ADO.NET Connection Pooling at a Glance http://support.microsoft.com/kb/310617/ ADO.NET数据连接池 关于ADO.Net连接池(Connection Pool)的一些个人见解 How to: Open Activity Monitor (SQL Server Management Studio) Connect Timeout = 0 / Connection Pool Timeout |
|
|