手贱-写链表玩玩

Posted on 2013-04-16 22:33  xiaomaogong  阅读(129)  评论(0编辑  收藏  举报
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataStructure
{
    public class DoubleNode<TData> 
    {
        public TData Data { get; set; }

        public DoubleNode<TData> Next { get; set; }

        public DoubleNode<TData> Previous { get; set; } 

        //Add 
        public void InsertToLast(DoubleNode<TData> node)
        {
            if (node == null)
                throw new Exception("Node can not be null");
            
            //represent last one
            if (this.Next == null)
            {
                this.Next = node;
                node.Previous = this;
            }
        }


        //
    }

    public class DoubleLink<TData>
    {
        public DoubleLink()
        {

        }

        //public DoubleLink(TData[] listNode)
        //{
        //    for (int i = 0; i < listNode.Length; i++)
        //    {
        //        var node = new DoubleNode<TData>(){Data = listNode[i]};
        //        if (i == 0)
        //        {
        //            this.Head = node;
        //        }
        //        else if (i == listNode.Length - 1)
        //        {
        //            this.Tail = node;
        //        }
        //    }
        //}
        
        public DoubleNode<TData> Head { get; set; }        
        public DoubleNode<TData> Tail { get; set; }

        public void AddToLast(DoubleNode<TData> node)
        {
            if (this.Tail != null)
            {
                var currenTail = this.Tail;
                currenTail.Next = node;
                node.Previous = currenTail;
                node.Next = null;
                this.Tail = node;
            }
            else
            {
                this.Tail = node;
                this.Head = node;
            }
        }

        public void RemoveNode(DoubleNode<TData> node)
        {
           //Control the Next of previous node 
          if (node.Previous != null) node.Previous.Next = node.Next; else this.Head = node.Next;
          //Control the previous of next Node if (node.Next != null) node.Next.Previous = node.Previous; else this.Tail = node.Previous; } public DoubleNode<TData> Find(TData data) { var targetNode = this.Head; while (targetNode != null) { if (data.Equals(targetNode.Data)) return targetNode; targetNode = targetNode.Next; } return null; } } }

  一下利用上面的双向链表写的栈:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace DataStructure
 8 {
 9     public class Stack<TData>
10     {
11         private DoubleLink<TData> dataList;
12         
13         public Stack()
14         {
15             dataList = new DoubleLink<TData>();
16         }
17 
18         public void Push(TData data)
19         {
20             dataList.AddToLast(new DoubleNode<TData>() { Data = data});
21         }
22 
23         public TData Pop()
24         {
25             var tail = dataList.Tail;
26             if (tail == null)
27                 return default(TData);
28 
29             dataList.RemoveNode(tail);
30             return tail.Data;
31         }
32     }
33 }

 

 by default live library includes winsock.h through windows.h instead of winsock2.h, but requires Winsock 2 in  initializeWinsockIfNecessary(...) function.
IP_ADD_MEMBERSHIP value for Winsock1 is 5, and for Winsock 2 is 12.
Therefore socketJoinGroup(...) function is trying to call setsockopt(IP_ADD_MEMBERSHIP) from Winsock 2 with optname from Winsock 1. 
There is the confusion between Winsock versions.


live555的 组播的code如下 : 

 testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary
      Port testPort(15947); // ditto

      sock = setupDatagramSocket(env, testPort);
      if (sock < 0) break;

      if (!socketJoinGroup(env, sock, testAddr.s_addr))
   {
    env<<"Failed to socket Join Group\n";
    break;
   }


结果:

 if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
   (const char*)&imr, sizeof (struct ip_mreq)) < 0) {

一直失败:错误提示为: 10042:

修改为: if (setsockopt(socket, IPPROTO_IP, 12,
   (const char*)&imr, sizeof (struct ip_mreq)) < 0) {


因为不同的socket版本,定义IP_ADD_MEMBERSHIP 的值不同:

in socket ver1.0中:
#define IP_ADD_MEMBERSHIP 5

in socket ver2.0中:
#define IP_ADD_MEMBERSHIP 12

 

 

socket receive 数据 一次性接收不全 问题

在发送端,一次发送4092个字节,

在接收端,一次接收4092个字节,

但是在接收端,偶尔会出现socket.receive接收不全的情况,ret = sockTemp.Receive(bBuffer,iBufferLen,0); //也有可能无法收到全部数据!

必须要考虑0 < ret < iBufferLen的情况:继续接收iBufferLen - ret字节,然后合并Socket的Send,Recv的长度问题:

 

一个包没有固定长度,以太网限制在46-1500字节,1500就是以太网的MTU,超过这个量,TCP会为IP数据报设置偏移量进行分片传输,现在一般可允许应用层设置8k(NTFS系统)的缓冲区,8k的数据由底层分片,而应用层看来只是一次发送。

windows的缓冲区经验值是4k。

Socket本身分为两种,流(TCP)和数据报(UDP),你的问题针对这两种不同使用而结论不一样。甚至还和你是用阻塞、还是非阻塞Socket来编程有关。

1、通信长度,

这个是你自己决定的,没有系统强迫你要发多大的包,实际应该根据需求和网络状况来决定。对于TCP,这个长度可以大点,但要知道,Socket内部默认的收发缓冲区大小大概是8K,你可以用SetSockOpt来改变。但对于UDP,就不要太大,一般在1024至10K。注意一点,你无论发多大的包,IP层和链路层都会把你的包进行分片发送,一般局域网就是1500左右,广域网就只有几十字节。分片后的包将经过不同的路由到达接收方,对于UDP而言,要是其中一个分片丢失,那么接收方的IP层将把整个发送包丢弃,这就形成丢包。显然,要是一个UDP发包佷大,它被分片后,链路层丢失分片的几率就佷大,你这个UDP包,就佷容易丢失,但是太小又影响效率。最好可以配置这个值,以根据不同的环境来调整到最佳状态。

send()函数返回了实际发送的长度,在网络不断的情况下,它绝不会返回(发送失败的)错误,最多就是返回0。对于TCP你可以写一个循环发送。当send函数返回SOCKET_ERROR时,才标志着有错误。但对于UDP,你不要写循环发送,否则将给你的接收带来极大的麻烦。所以UDP需要用SetSockOpt来改变Socket内部Buffer的大小,以能容纳你的发包。明确一点,TCP作为流,发包是不会整包到达的,而是源源不断的到,那接收方就必须组包。而UDP作为消息或数据报,它一定是整包到达接收方。

 

2、关于接收,

一般的发包都有包边界,首要的就是你这个包的长度要让接收方知道,于是就有个包头信息,对于TCP,接收方先收这个包头信息,然后再收包数据。一次收齐整个包也可以,可要对结果是否收齐进行验证。这也就完成了组包过程。UDP,那你只能整包接收了。要是你提供的接收Buffer过小,TCP将返回实际接收的长度,余下的还可以收,而UDP不同的是,余下的数据被丢弃并返回WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer太小,而一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时,密切注意这点。这些特性就是体现了流和数据包的区别。

补充一点,接收BuffSize >=发送BuffSize >=实际发送Size,对于内外部的Buffer都适用,上面讲的主要是Socket内部的Buffer大小关系。

3、TCP是有多少就收多少,如果没有当然阻塞Socket的recv就会等,直到有数据,非阻塞Socket不好等,而是返回WSAEWOULDBLOCK。UDP,如果没有数据,阻塞Socket就会等,非阻塞Socket也返回WSAEWOULDBLOCK。如果有数据,它是会等整个发包到齐,并接收到整个发包,才返回。

Copyright © 2024 xiaomaogong
Powered by .NET 9.0 on Kubernetes