• 2008-04-02

    C# 中实现 FIFO 缓冲区–ArrayBuffer

    Views: 11674 | No Comments

    .Net 似乎没有 FIFO 的内存缓冲类, 所以我实现了这样的一个类 ArrayBuffer. 该类把一个定长的数组(可重新分配)作为环形队列.

    使用实例:

    class Test
    {
        // 缓冲的容量为 7 个单位.
        private static ArrayBuffer<byte> ab = new ArrayBuffer<byte>(7);
    
        private static Random rand = new Random();
    
        public static void Main(string[] args){
            Thread t = new Thread(Reader);
            t.IsBackground = true;
            t.Start();
    
            for(int i=0; i<12; i++){
                byte[] data = Encoding.UTF8.GetBytes(i + ", ");
                ab.Write(data);
                Console.WriteLine("Write: " + i + ",");
                Thread.Sleep(rand.Next(200));
            }
        }
    
        private static void Reader(){
            byte[] readBuf = new byte[512];
            int n_read = 0;
            while(true){
                n_read = ab.Read(readBuf);
                string s = Encoding.UTF8.GetString(readBuf, 0, n_read);
                Console.WriteLine("Read: " + s);
                Thread.Sleep(rand.Next(300));
            }
        }
    }
    
    /*
    打印出下面类似的信息:
    Write: 0,
    Read: 0,
    Write: 1,
    Read: 1,
    Write: 2,
    Write: 3,
    Read: 2, 3,
    Write: 4,
    */
    

    ArrayBuffer 完整源码:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    
    namespace Ideawu.Utils
    {
        /**
         * 缓冲区的可能状态:
         * <code>
         * (1)
         * ----====== data ======-----rspace----
         *     |                 |             |
         *     rd_nxt            wr_nxt        capacity-1
         *(2)
         * ==ldata==-------------==== rdata ====
         *          |            |             |
         *          wr_nxt       rd_nxt        capacity-1
         * (3)
         * ===ldata=============rdata===========(full of data)
         *             |
         *             wr_nxt(rd_nxt)
         * (4)
         * -------------------------------------(empty)
         *           |
         *           wr_nxt(rd_nxt)
         * </code>
         */
    
        /// <summary>
        /// 使用字节数组来实现的缓冲区. 该缓冲区把该数组看作是一个环,
        /// 支持在一块固定的数组上的无限次读和写, 数组的大小不会自动变化.
        /// </summary>
        /// <typeparam name="T">所缓冲的数据类型.</typeparam>
        public class ArrayBuffer<T>
        {
            /// <summary>
            /// 默认大小.
            /// </summary>
            private const int DFLT_SIZE = 512 * 1024;
    
            /// <summary>
            /// 缓冲区还能容纳的元素数目.
            /// </summary>
            private int space = 0;
    
            /// <summary>
            /// 缓冲区中的数据元素数目.
            /// </summary>
            private int available = 0;
    
            /// <summary>
            /// 缓冲区的容量.
            /// </summary>
            private int capacity = DFLT_SIZE;
            // 注意 capacity 和 buf.Length 可以不相同, 前者小于或者等于后者.
    
            /// <summary>
            /// 下一次要将数据写入缓冲区的开始下标.
            /// </summary>
            private int wr_nxt = 0;
    
            /// <summary>
            /// 下一次读取接收缓冲区的开始下标.
            /// </summary>
            private int rd_nxt = 0;
    
            private int readTimeout = -1;
    
            private int writeTimeout = -1;
    
            private Semaphore writeSemaphore = new Semaphore(1, 1);
    
            /// <summary>
            /// 缓冲区所使用的数组.
            /// </summary>
            private T[] dataBuf;
    
            private object bufLock = new object();
    
            /// <summary>
            /// 如果当前缓冲区中有数据可读, 它将会被设置.
            /// </summary>
            private Semaphore readSemaphore = new Semaphore(0, 1);
    
            /// <summary>
            /// 创建一个具体默认容量的缓冲区.
            /// </summary>
            public ArrayBuffer()
                : this(DFLT_SIZE) {
            }
    
            /// <summary>
            /// 创建一个指定容量的缓冲区.
            /// </summary>
            /// <param name="capacity">缓冲区的容量.</param>
            public ArrayBuffer(int capacity)
                : this(new T[capacity]) {
            }
    
            /// <summary>
            /// 使用指定的数组来创建一个缓冲区.
            /// </summary>
            /// <param name="buf">缓冲区将要使用的数组.</param>
            public ArrayBuffer(T[] buf)
                : this(buf, 0, 0) {
            }
    
            /// <summary>
            /// 使用指定的数组来创建一个缓冲区, 且该数组已经包含数据.
            /// </summary>
            /// <param name="buf">缓冲区将要使用的数组.</param>
            /// <param name="offset">数据在数组中的偏移.</param>
            /// <param name="size">数据的字节数.</param>
            public ArrayBuffer(T[] buf, int offset, int size) {
                this.dataBuf = buf;
                capacity = buf.Length;
                available = size;
                space = capacity - available;
                rd_nxt = offset;
                wr_nxt = offset + size;
            }
    
            /// <summary>
            /// 缓冲区还能容纳的元素数目.
            /// </summary>
            public int Space {
                get {
                    return space;
                }
            }
    
            /// <summary>
            /// 缓冲区中可供读取的数据的元素数目
            /// </summary>
            public int Available {
                get {
                    return available;
                }
            }
    
            /// <summary>
            /// get, set 接收缓冲区的大小(元素数目). 默认值为 512K.
            /// Capacity 不能设置为小于 Available 的值(实现会忽略这样的值).
            /// </summary>
            public int Capacity {
                get {
                    return capacity;
                }
                set {
                    lock (bufLock) {
                        if (value < available || value == 0) {
                            return;
                            //throw new ApplicationException("Capacity must be larger than Available.");
                        }
                        if (value == capacity) {
                            return;
                        }
                        if (value > capacity && space ==0) {
                            // 可写空间变为非空, 释放可写信号.
                            writeSemaphore.Release();
                        }
    
                        T[] buf = new T[value];
                        if (available > 0) {
                            available = ReadData(buf, 0, buf.Length);
                            // 下面的用法是错误的!
                            //available = Read(buf, 0, buf.Length);
                        }
                        dataBuf = buf;
                        capacity = value;
                        space = capacity - available;
                        rd_nxt = 0;
                        // 当容量缩小时, 可能导致变化后可写空间为0, 这时wr_nxt=0.
                        wr_nxt = (space == 0) ? 0 : available;
                    }
                }
            }
    
            /// <summary>
            /// Read 方法的超时时间(单位毫秒). 默认为 -1, 表示无限长.
            /// </summary>
            public int ReadTimeout {
                get {
                    return readTimeout;
                }
                set {
                    readTimeout = value;
                }
            }
    
            /// <summary>
            /// Write 方法的超时时间(单位毫秒). 默认为 -1, 表示无限长.
            /// </summary>
            public int WriteTimeout {
                get {
                    return writeTimeout;
                }
                set {
                    writeTimeout = value;
                }
            }
    
            /// <summary>
            /// 清空本缓冲区.
            /// </summary>
            public void Clear() {
                lock (bufLock) {
                    available = 0;
                    space = capacity;
                    rd_nxt = 0;
                    wr_nxt = 0;
                }
            }
    
            /*
            /// <summary>
            /// 将读指针向前移动 num 个单元. 如果 num 大于 Avalable,
            /// 将抛出异常.
            /// </summary>
            /// <param name="num">读指针要向前的单元个数.</param>
            /// <exception cref="ApplicationException">num 大于 Avalable.</exception>
            public void Seek(int num) {
            }
            */
    
            /// <summary>
            /// 未实现.
            /// </summary>
            /// <returns></returns>
            public T ReadOne() {
                throw new Exception("Not supported.");
            }
    
            /// <summary>
            /// 从缓冲区中读取数据. 读取的字节数一定是 buf.Length 和 Available 的较小者.
            /// </summary>
            /// <param name="buf">存储接收到的数据的缓冲区.</param>
            /// <returns>已经读取的字节数. 一定是 size 和 Available 的较小者.</returns>
            public int Read(T[] buf) {
                return Read(buf, 0, buf.Length);
            }
    
            /// <summary>
            /// 从缓冲区中读取数据. 读取的字节数一定是 size 和 Available 的较小者.
            /// 本方法是线程安全的.
            /// </summary>
            /// <param name="buf">存储接收到的数据的缓冲区.</param>
            /// <param name="offset">buf 中存储所接收数据的位置.</param>
            /// <param name="size">要读取的字节数.</param>
            /// <returns>已经读取的字节数. 一定是 size 和 Available 的较小者.</returns>
            public int Read(T[] buf, int offset, int size) {
                if (!readSemaphore.WaitOne(readTimeout, false)) {
                    throw new ApplicationException("Read timeout.");
                }
    
                lock (bufLock) {
                    int nread = ReadData(buf, offset, size);
                    if (space == 0) {
                        // 释放可写信号.
                        writeSemaphore.Release();
                    }
                    space += nread;
                    available -= nread;
                    if (available > 0) {
                        // 释放一个信号, 以便下一次再读.
                        readSemaphore.Release();
                    }
                    return nread;
                }
            }
    
            /// <summary>
            /// 把本缓冲区的数据复制指定的数组中, 并移动读指针.
            /// </summary>
            private int ReadData(T[] buf, int offset, int size) {
                int nread = (available >= size) ? size : available;
                // 当 rd_nxt 在 wr_nxt 的左边时, 缓冲的右边包含的网络字节数.
                int rdata = capacity - rd_nxt;
                if (rd_nxt < wr_nxt || rdata >= nread/*隐含rd_nxt >= wr_nxt*/) {
                    Array.Copy(dataBuf, rd_nxt, buf, offset, nread);
                    rd_nxt += nread;
                } else {
                    // 两次拷贝.
                    Array.Copy(dataBuf, rd_nxt, buf, offset, rdata);
                    rd_nxt = nread - rdata;
                    Array.Copy(dataBuf, 0, buf, offset + rdata, rd_nxt);
                }
                return nread;
            }
    
            /// <summary>
            /// 写入数据到缓冲区.
            /// </summary>
            /// <param name="buf">要写入的数据的缓冲区.</param>
            public void Write(byte[] buf) {
                Write(buf, 0, buf.Length);
            }
    
            /// <summary>
            /// 写入数据到缓冲区. 注意: 本方法不是线程安全的.
            /// </summary>
            /// <param name="buf">要写入的数据的缓冲区.</param>
            /// <param name="offset">数据缓冲区中要写入数据的起始位置.</param>
            /// <param name="size">要写入的字节数.</param>
            /// <exception cref="ApplicationException">如果空间不足, 会抛出异常.</exception>
            public void Write(byte[] buf, int offset, int size) {
                int n_left = size;
                int n_offset = offset;
                int nwrite;
                int rspace;
                while (n_left > 0) {
                    // 这样的超时控制并不准确!
                    if (!writeSemaphore.WaitOne(writeTimeout, false)) {
                        throw new ApplicationException("Write timeout.");
                    }
    
                    lock (bufLock) {
                        nwrite = (space >= n_left) ? n_left : space;
                        // 当 rd_nxt 在 wr_nxt 的左边时, 缓冲的右边可以放置的网络字节数.
                        rspace = capacity - wr_nxt;
                        if (wr_nxt < rd_nxt || rspace >= nwrite/*隐含wr_nxt >= rd_nxt*/) {
                            Array.Copy(buf, n_offset, dataBuf, wr_nxt, nwrite);
                            wr_nxt += nwrite;
                            if (wr_nxt == capacity) {
                                wr_nxt = 0;
                            }
                        } else {
                            // 两次拷贝.
                            Array.Copy(buf, n_offset, dataBuf, wr_nxt, rspace);
                            wr_nxt = nwrite - rspace; // 是调用下一句之后的 wr_nxt值.
                            Array.Copy(buf, n_offset + rspace, dataBuf, 0, wr_nxt);
                        }
                        if (available == 0) {
                            readSemaphore.Release();
                        }
                        space -= nwrite;
                        available += nwrite;
                        if (space > 0) {
                            // 释放可写信号.
                            writeSemaphore.Release();
                        }
    
                        n_offset += nwrite;
                        n_left -= nwrite;
                    }
                } // end while
    
                /* 不需要 WriteTimeout 的版本.
                // 和 Read 是对称的.
                lock (bufLock) {
                    if (space < size) {
                        // TBD: 是否实现写超时机制?
                        throw new ApplicationException("Not enough space.");
                    }
    
                    // 当 wr_nxt 在 rd_nxt 的左边时, 缓冲的右边可以放置的网络字节数.
                    int rspace = capacity - wr_nxt;
                    if (wr_nxt < rd_nxt || rspace >= size) {
                        Array.Copy(buf, offset, dataBuf, wr_nxt, size);
                        wr_nxt += size;
                    } else {
                        // 两次拷贝.
                        Array.Copy(buf, offset, dataBuf, wr_nxt, rspace);
                        wr_nxt = size - rspace;
                        Array.Copy(buf, offset + rspace, dataBuf, 0, wr_nxt);
                    }
                    if (available == 0) {
                        readSemaphore.Release();
                    }
                    space -= size;
                    available += size;
                }
                */
            }
    
        }
    
    }
    
    Posted by ideawu at 2008-04-02 19:46:03
  • 2008-03-22

    C# 版的 SimpleXML

    Views: 9791 | No Comments

    PHP 的 SimpleXML 类让你可以使用对象操作的方法操作 XML, 这是非常方便的, 想一想, a->b 与 a.getChild('b') 哪个更方便. 因为在 C# 中需要操作简单的 XML, 所以我试图实现一个 C# 版的 SimpleXML. 因为 C# 是一种静态型语言, 无法动态地生成一个不存在的类的实例, 所以我使用哈希表(字典, Dictionary)来实现, 正由于 C# 支持索引器, 我的 C# 版的 SimpleXML 类似 PHP 的关联数组. 使用例子如下:

    SimpleXml xml = new SimpleXml("<root><a>Hello World!</a></root>");
    Console.WriteLine(xml["a"]); // Hello World!
    

    完整的源码:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Collections;
    using System.IO;
    using System.Xml;
    
    namespace Ideawu.P2P.Utils
    {
        /// <summary>
        /// 表示一个 XML 节点. 以关联数组的方式操作 XML, 类似 PHP 的 SimpleXML.
        /// </summary>
        public class SimpleXml : IEnumerable
        {
            private String name = "";
    
            private String text = "";
    
            /// <summary>
            /// 表明该节点是否是同名节点的列表.
            /// </summary>
    
            private bool isList = false;
    
            private Dictionary<string, SimpleXml> children = new Dictionary<String, SimpleXml>();
    
            private Dictionary<string, string> attributes = new Dictionary<string, string>();
    
            /// <summary>
            /// 创建一个空的 SimpleXml.
            /// </summary>
    
            public SimpleXml() {
            }
    
            /// <summary>
            /// 以指定的名称创建一个 SimpleXml.
            /// </summary>
            /// <param name="name">SimpleXml 的名称.</param>
            public SimpleXml(string name) {
                this.name = name;
            }
    
            /// <summary>
    
            /// 以指定的名称和文本内容创建一个 SimpleXml.
            /// </summary>
            /// <param name="name">SimpleXml 的名称.</param>
            /// <param name="text">SimpleXml 的文本内容.</param>
            public SimpleXml(string name, string text) {
                this.name = name;
                this.text = text;
            }
    
            /// <summary>
    
            /// 名称, 标签名.
            /// </summary>
            public String Name {
                get {
                    return name;
                }
                set {
                    name = value;
                }
            }
    
            /// <summary>
            /// 节点的文本(经过 HTML encode).
            /// </summary>
            public String Text {
                get {
                    return text;
                }
                set {
                    text =  value;
                }
            }
    
            /// <summary>
            /// 表明该节点是否是同名节点的列表.
            /// </summary>
    
            public bool IsList {
                get {
                    return isList;
                }
            }
    
            /// <summary>
            /// 获取指定名称的属性值.
            /// </summary>
            /// <param name="key">属性名</param>
            /// <returns>属性值. 如果不存在则为空.</returns>
    
            public string GetAttribute(String key) {
                string v = null;
                attributes.TryGetValue(key, out v);
                return v;
            }
    
            /// <summary>
            /// 为节点添加一个属性.
            /// </summary>
            /// <param name="key">属性名.</param>
            /// <param name="value">属性值.</param>
    
            public void SetAttribute(String key, String value) {
                attributes[key] = value;
            }
    
            /// <summary>
            /// 删除指定名称的属性.
            /// </summary>
            /// <param name="key">要删除的属性的名称.</param>
            public void RemoveAttribute(String key) {
                attributes.Remove(key);
            }
    
            /// <summary>
    
            /// 移除本节点的所有属性.
            /// </summary>
            public void RemoveAllAttribute() {
                attributes.Clear();
            }
    
            /// <summary>
            /// 获取指定标签名的子节点. 如果要获取的子节点不存在, 则返回 null.
            /// 这和通过索引器获取不一样.
            /// </summary>
            /// <param name="name">子节点的标签名.</param>
    
            /// <returns>子节点的引用或者 null.</returns>
            public SimpleXml GetChild(string name) {
                SimpleXml c = null;
                children.TryGetValue(name, out c);
                return c;
            }
    
            /// <summary>
            /// 添加一个带有指定标签名和文本的子节点.
            /// </summary>
            public void AddChild(string name, string text) {
                SimpleXml node = new SimpleXml(name, text);
                this[name] = node;
            }
    
            /// <summary>
    
            /// 移除本节点的指定名称(标签名)的子节点.
            /// </summary>
            /// <param name="childName">要移除的子节点的标签名.</param>
            public void RemoveChild(string childName) {
                children.Remove(childName);
            }
    
            /// <summary>
            /// 移除本 SimpleXml 的所有子节点.
            /// </summary>
    
            public void RemoveAllChild() {
                children.Clear();
            }
    
            /// <summary>
            /// 清除本 Simple 所对应的 XML 标签的所有属性和子节点, 但不包括标签本身.
            /// 效果和同时调用 RemoveAllChild 和 RemoveAllAttribute 一样.
            /// </summary>
            public void Clear() {
                children.Clear();
                attributes.Clear();
            }
    
            /// <summary>
            /// 根据子节点的标签名获取子节点.
            /// </summary>
            /// <param name="childName"></param>
    
            /// <returns></returns>
            public SimpleXml this[string childName] {
                get {
                    SimpleXml obj;
                    if (!children.TryGetValue(childName, out obj)) {
                        obj = new SimpleXml();
                        // 让空的节点名表示节点不存在. 而返回一个 SimpleXml 又可以使不抛出 NullReferenceException.
                        //obj.name = childName;
                    }
                    return obj;
                }
                set {
                    SimpleXml obj;
                    if (!children.TryGetValue(childName, out obj)) {
                        children[childName] = value;
                    } else {
                        // 如果存在相同名字的节点, 创建一个列表类型的节点, 包含所有这些同名节点,
                        // 在内部以 0, 1, 2...命名.
                        /**
                         * 例如: <doc><bs><b/><b/></bs></doc> 可以这样迭代:
                         * foreach(n in xml["bs"]){} 或者 foreach(n in xml["bs"]["b"]){}
                         */
                        if (!obj.isList) {
                            SimpleXml list = new SimpleXml();
                            list.isList = true;
                            list.name = obj.name;
                            list.text = obj.text;
                            children[childName] = list;
    
                            list.children["0"] = obj;
                            list.children["1"] = value;
                        } else {
                            obj.children[obj.children.Count.ToString()] = value;
                        }
                    }
                }
            }
    
            #region IEnumerable 成员
    
            /// <summary>
    
            /// 本 SimpleXml 的所有子节点的迭代.
            /// </summary>
            public IEnumerator GetEnumerator() {
                foreach (SimpleXml obj in children.Values) {
                    if (obj.isList && !this.isList) {
                        foreach (SimpleXml c in obj.children.Values) {
                            yield return c;
                        }
                    } else {
                        this.GetEnumerator();
                        yield return obj;
                    }
                }
            }
    
            #endregion
    
            /// <summary>
            /// 返回本节点的名称(标签名).
            /// </summary>
            /// <returns>本节点的名称(标签名).</returns>
    
            public override String ToString() {
                return text;
            }
    
            /// <summary>
            /// 将本 SimpleXml 转换成 XML 字符串.
            /// </summary>
            /// <returns>XML 字符串表示的 SimpleXml.</returns>
            public string AsXml() {
                StringBuilder sb = new StringBuilder();
                AsXml(this, sb);
                return sb.ToString();
            }
    
            /// <summary>
    
            /// 将本 SimpleXml 所表示的 XML 文本写入到 StringBuilder 中.
            /// </summary>
            /// <param name="sb">要写入的 StringBuilder.</param>
            public void WriteTo(StringBuilder sb) {
                AsXml(this, sb);
            }
    
            private void AsXml(SimpleXml node, StringBuilder sb) {
                sb.Append("<" + node.name);
                if (node.attributes.Count > 0) {
                    foreach (KeyValuePair<String, String> kv in node.attributes) {
                        sb.Append(String.Format(" {0}=\"{1}\"", kv.Key, kv.Value));
                    }
                }
                sb.Append(">");
    
                sb.Append(System.Web.HttpUtility.HtmlEncode(node.text));
    
                foreach (SimpleXml child in node.children.Values) {
                    if (child.isList) {
                        foreach (SimpleXml cc in child) {
                            AsXml(cc, sb);
                        }
                    } else {
                        AsXml(child, sb);
                    }
                }
    
                sb.Append("</" + node.name + ">");
            }
    
            /// <summary>
    
            /// 从指定的流中载入 XML 文档来构建本 SimpleXml.
            /// </summary>
            /// <param name="stream">要载入的流.</param>
            public void Load(Stream stream) {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(stream);
                this.Load(xmldoc);
            }
    
            /// <summary>
            /// 从指定的 XmlDocument 实例中载入 XML 文档来构建本 SimpleXml.
            /// </summary>
    
            /// <param name="xmldoc">要载入的 XmlDocument 实例.</param>
            public void Load(XmlDocument xmldoc) {
                XmlElement e = xmldoc.DocumentElement;
                SimpleXml node = LoadXmlElement(xmldoc.DocumentElement);
                this.name = node.name;
                this.text = node.text;
                this.children = node.children;
                this.attributes = node.attributes;
            }
    
            /// <summary>
            /// 从指定的字符串中载入 XML 文档来构建本 SimpleXml.
            /// </summary>
            /// <param name="stream">要载入的字符串.</param>
    
            public void LoadXml(string xmlstr) {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlstr);
                this.Load(doc);
            }
    
            private SimpleXml LoadXmlElement(XmlElement e) {
                SimpleXml node = new SimpleXml();
                node.name = e.Name;
    
                if (e.HasAttributes) {
                    foreach (XmlAttribute attr in e.Attributes) {
                        node.attributes[attr.Name] = attr.Value;
                    }
                }
    
                if (e.HasChildNodes) {
                    foreach (XmlNode c in e.ChildNodes) {
                        switch (c.NodeType) {
                            case XmlNodeType.CDATA:
                            case XmlNodeType.Text:
                                node.text += c.Value;
                                break;
                            case XmlNodeType.Element:
                                SimpleXml child = LoadXmlElement((XmlElement)c);
                                node[child.name] = child;
                                break;
                        }
                    }
                }
    
                return node;
            }
        }
    
    }
    
    Posted by ideawu at 2008-03-22 19:53:36
  • 2007-12-18

    腾讯QQ屏蔽.CN域名的行为莫名奇妙,不当和违法

    Views: 8215 | No Comments

    据IT传媒网的报道(http://www.cniter.com/news/200712/13030.html), 腾讯的即时通讯工具QQ屏蔽了带有.CN域名的用户正常交谈的消息, 并且不给予任何提示.

    经测试, 这个报道属实. 腾讯的作法实在是令人莫名奇妙. .CN域名是国家合法的域名, 并且已经被广泛使用, 屏蔽.CN域名会造成正常的交流无法进行. 该报道中称, 腾讯的合作伙伴Google中国的域名(google.cn)没有被屏蔽. 还有域名注册公司万网的域名(www.net.cn)也没有被屏蔽, 但是"net.cn"被屏蔽了(这表明腾讯对域名和URL的判断技术和IE6一样落后!). 相信其它所有的.CN域名都被屏蔽了.

    腾讯的作法的不当之处在于, 当消息被屏蔽之后, 软件并不给出任何提示, 一条正常的消息就从网络线路上被腾讯公司给删除了. 在网络上搜索, 可以发现, 有人因此朋友间产生误会, 也有人因此丢掉生意. 这也表明, 虽然QQ是P2P的IM工具, 但是腾讯似乎过滤了我们发送和接收的每一条信息.

    而且, 腾讯的这种作法本人认为是违法的. 因为,

    1. 带有.CN域名的消息是用户的个人物品(财产), 是正常和合法的, 腾讯没有权利截获并从通信线路上删除.
    2. 腾讯在进行这个作法之前, 没有通知用户. 而且在屏蔽用户消息时未给用户提示信息, 单方面更改了对用户提供可靠通信服务的承诺. 如果腾讯从未作过这类承诺, 应该以双方长期的默契作为依据.

    因为腾讯的这个作法而导致生意没有谈成或者其它财产损失的人, 无论是QQ的免费或者收费用户, 我认为都可以向腾讯公司进行索赔. 不过, 这方面在法律上空间太大.

    Posted by ideawu at 2007-12-18 22:24:35
  • 2007-11-18

    “纸老虎”事件和”陕西假宝马彩票案”

    Views: 9886 | 1 Comment

    将"纸老虎"和"假宝马"联系起来有些牵强, 但两者的涉案人都有政府部门.

    目前, 陕西省相关部门似乎想转移视线. 民众关注的是照片中老虎的真假, 他们却只谈照片的真假和老虎是否存在. 有某个无耻的官员竟然骂别人"说老虎的存在和照片的真假是两码事是强盗逻辑", 却不知自己正是强盗逻辑!

    随着事情的发展, 当事人会再玩文字游戏, 但是, 法律虽然以文字的形式出现, 却不会任由某些人玩文字游戏的.

    2004年西安宝马彩票案主犯杨永明终审被判19年.

    ------
    2009年10月10日更新: "纸老虎", 后来有一个更贴切的名称: "周老虎".

    Posted by ideawu at 2007-11-18 11:42:27
  • 2007-11-07

    “汉语编程”应该是个法律概念

    Views: 7644 | No Comments

    最近, "汉语编程"的出镜率似乎有些高. 我记得, 一年多前也有这种情况:

    我认为是某些利益群体在炒作. 而且, 一个可怕的信号表明, 他们开始使用不公开的手段将他们的商品推销到没有判断能力的中小学生身上. 前段时间, 有报道称大连的教育部门把某种"汉语编程"语言作为小学教材.

    当前, "汉语编程"并没有明确的国家标准, 也没有通用的行业标准. 这也是我为什么通篇在"汉语编程"这个词组的两边使用引号的原因. 对一些不成型的新技术, 可以进行试验性的推广, 但是手段必须是合法的, 所有人普遍认可的. 某些人将不成熟的, 不被行业认可的东西推广到中小学生等没有判断能力的人身上, 不能不让人警惕.

    所以, 不像其他朋友从技术方面讨论当前市面上所谓的"汉语编程"--我认为其本质上没有任何技术含量, 不值一提--我更希望更多的人从法律上对"汉语编程"进行关注, 确保其背后的商人没有使用不合法的手段, 以及不危害到中小学生的教育.

    Posted by ideawu at 2007-11-07 21:19:02
  • 2007-09-19

    什么是 goodle 和 goodle Earth?

    Views: 13475 | 1 Comment

    查看我的网站的访问记录, 发现很多来自百度的"goodle earth". 原来是 google 的错误拼写. 查看 goodle.com 这个网站, 是一个韩国网站, 不知道该网站现在值多少钱.

    Google Earth 的下载链接在: http://earth.google.com

    Posted by ideawu at 2007-09-19 19:33:18
|<<<456789101112>>>| 8/15 Pages, 85 Results.