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。如果有数据,它是会等整个发包到齐,并接收到整个发包,才返回。