internal override void InternalWormHole(object obj)
        {
            //文件开始发送时,会调用这个函数。所以ProcessingItems里面存储的,是所有尝试开始过的Item,如果成功了,就会从里面移除,不管是主动发送完毕,还是对方已经有这个文件,直接变成完成状态。

            //TODO 现在有个问题没有解决,获取正在等待确认的文件,或者出错的文件,是应该在这个ProcessingItems里面呢,还是应该去Items属性里面?因为这两个是重复的。
            //作为临时的解决方案,可以查找ProcessingItems里面的Item的相对路径是不是"", 如果是,就不要显示,因为Items那里已经显示了。

            //TODO 重构 有没有更好的解决方案?不要冒然删除ProcessingItems,因为Items属性只管理了顶层的文件、文件夹。嵌套的子文件在那里面查找不到。
            if (obj is FileItem)
            {
                //这里的item可以是嵌套很深的文件。在构造requester的时候设置item的回调达不到这个效果,除非遍历子元素。
                //文件等待确认或者出错,会转移到PendingItems里面去。会话会尽量完成能传输完毕的文件,传不成功的(出错或没有得到及时确认)会留在这里。
                var fi = obj as FileItem;
                if (ProcessingItems.Find(i => { return(i.ID == fi.ID); }) != null || fi.TransferState == TransferState.Confirmed)
                {
                    return;                                                                                                              //如果滥用了InternalWormHole,这样可以避免一下,否则会导致事件被触发多次。
                }
                ProcessingItems.Add(fi);
                fi.Confirmed += (o) => ProcessingItems.Remove(fi);
                fi.Completed += (o) =>
                {
                    if (fi.NeedConfirm)
                    {
                        fi.TransferState = TransferState.WaitingConfirm;
                    }
                };
            }
        }
        //速度可以计算出来?用传送开始时间、传输的数据量,再用一个timer定时更新速度就可以了。更新总的速度,或者一个会话的速度都可以。
        //public event Action<int> SpeedChanged;
        internal protected override void OnInitRequest()
        {
            SendItemsMessage message = new SendItemsMessage(Items);

            Items.ForEach(item =>
            {
                if (item is FileItem)
                {
                    ProcessingItems.Add(item);
                }
            });
            PostMessage(message);
        }