UPD可靠传输的算法 VR资源

摘要

UDP大家都知道是什么东东吧,它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。[引用自互动百科]它最突出的一个优点,就是整包送达,不像TCP那样,还需要考虑粘包和拆包,并且UDP通讯是公平的,没有所谓的服务器或者客户端,想发就发,要发的漂亮。 但是它也有很严重的缺点,那就是——丢包率,因为它是不可靠的通信,发出去之后,就不管了。

UDP大家都知道是什么东东吧,它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。[引用自互动百科]它最突出的一个优点,就是整包送达,不像TCP那样,还需要考虑粘包和拆包,并且UDP通讯是公平的,没有所谓的服务器或者客户端,想发就发,要发的漂亮。 但是它也有很严重的缺点,那就是——丢包率,因为它是不可靠的通信,发出去之后,就不管了。





所以我们如果想用它来做一些简单的东西还好,丢就丢了,不过想做成可用的游戏或者应用,又不太想用TCP(就你事儿多),就必须要想办法克服它的不可靠这个缺点了。



最简单的一个思路,就是将所有要发送的包,放到一个队列里,另外一端收到这个包之后,就告诉发包端,我收到了,你可以把包XXX从队列里删除掉了,否则发包端就会一直在一定时间后重发这个包,一般我们还会设定发送若干次之后将这个包主动丢弃,因为可能已经过了时效。当然,这个确认包我们一般设置为不可靠传输,否则这个确认包收到后再发个“确认‘确认包’包”,就没个完了。



另外一个小事情,UDP的传输是有字节数上限的,一般是65535个字节,但是UDP本身的包头(8)和IP包头(20)一共会占去28个字节,也就是说我们能用的最大上限是65507个byte。一般情况下是够用的,不过也还是有不少情况下需要将这些大包拆分成小包来发送。注意,这里和TCP协议的拆包是有本质上不同的,TCP协议的拆包是在接收端进行的,原因是TCP是个流协议,流是没有边界的,它会按照缓冲区的情况,来进行包的划分,它并不在意它划分出来的包是一个完整的包,还是一个大包的一部分,或是一堆小包的集合。而UDP协议的拆包,是在发送时要做好的,原因就是上面提到的,UDP传输是整包发送,并且有字节数上限。





闲话少说,上源码。

AUdpConnector.cs

?
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
using System.Collections.Generic;
using System.Net.Sockets;
using System;
using System.Net;
using System.Text;
using System.Threading;
 
public class AUdpConnector
{
        private static AUdpConnector sinstance;
        public static AUdpConnector Instance
        {
                get
                {
                        if (sinstance == null)
                        {
                                sinstance = new AUdpConnector();
                        }
                        return sinstance;
                }
        }
 
        int bufferSize = 4096;
        UdpClient connector;
        Action<byte[], IPEndPoint, int> delReceive;
        public void Init(Action<byte[], IPEndPoint, int> receiveDel, int port)
        {
                delReceive = receiveDel;
                OnBind(port);
        }
        private void OnBind(int port)
        {
                connector = new UdpClient(port, AddressFamily.InterNetwork);
                OnStartReceive(connector);
                OnStartToSend();
        }
 
        private void OnStartToSend()
        {
                new Thread(new ThreadStart(DoSend)).Start();
        }
 
        public static bool bQuit = false;
        private void DoSend()
        {
                while (true)
                {
                        if (bQuit)
                        {
                                break;
                        }
                        lock (sendLock)
                        {
                                List<int> remove = new List<int>();
                                foreach (var pv in packetsToSendPool)
                                {
                                        var p = pv.Value;
                                        if (p.bOutOfTime)
                                        {
                                                remove.Add(pv.Key);
                                                continue;
                                        }
                                        if (p.NeedRetry)
                                        {
                                                byte[] buffer = new byte
;
                                                byte[] packetMetaIDBuff = BitConverter.GetBytes(p.packetMetaID);
                                                byte[] packetIDBuff = BitConverter.GetBytes(p.packetID);
                                                Array.ConstrainedCopy(packetMetaIDBuff, 0, buffer, 0, packetMetaIDBuff.Length);
                                                Array.ConstrainedCopy(packetIDBuff, 0, buffer, 4, packetIDBuff.Length);
                                                Array.ConstrainedCopy(p.buffer, 0, buffer, 8, p.buffer.Length);
                                                connector.Send(buffer, buffer.Length, p.endpoint);
 
                                                if (!p.bReliable)
                                                {
                                                        remove.Add(pv.Key);
                                                }
                                        }
                                }
                                foreach (var r in remove)
                                {
                                        packetsToSendPool.Remove(r);
                                }
                        }
 
                        Thread.Sleep(1);
                }
        }
 
        public void OnStartReceive(UdpClient s)
        {
                if (s == null)
                {
                        s = connector;
                }
 
                byte[] bs = new byte[bufferSize];
                s.BeginReceive(OnReceived, new object[] { s, bs });
        }
        private void OnReceived(IAsyncResult ar)
        {
                object[] objs = ar.AsyncState as object[];
                UdpClient s = objs[0] as UdpClient;
                IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
                byte[] bs = s.EndReceive(ar, ref sender);
                int buffr = bs.Length;
                if (buffr == 0)
                {
                        if (delReceive != null)
                        {
                                delReceive(null, sender, 0);
                        }
                }
                else
                {
                        if (delReceive != null)
                        {
                                lock (sendLock)
                                {
                                        int packetMetaID = BitConverter.ToInt32(bs, 0);
                                        int packetID = BitConverter.ToInt32(bs, 4);
 
                                        int totalSec = BitConverter.ToInt32(bs, 8);
                                        if (totalSec == 0)
                                        {
                                                int def = BitConverter.ToInt32(bs, 16);
 
                                                byte[] packetBuff = new byte[bs.Length - 20];
                                                Array.Copy(bs, 20, packetBuff, 0, packetBuff.Length);
                                                if (def == (int)EPacketID.EUDPPacketReceived)
                                                {
                                                        string sp = System.Text.Encoding.UTF8.GetString(packetBuff);
                                                        int ipid = 0;
                                                        int.TryParse(sp, out ipid);
                                                        if (packetsToSendPool.ContainsKey(ipid))
                                                        {
                                                                packetsToSendPool.Remove(ipid);
                                                        }
                                                }
                                                else
                                                {
                                                        OnSend("" + packetMetaID, sender.Address, sender.Port, (int)EPacketID.EUDPPacketReceived, false);
                                                        delReceive(packetBuff, sender, def);
                                                }
                                        }
                                        else
                                        {
                                                OnSend("" + packetMetaID, sender.Address, sender.Port, (int)EPacketID.EUDPPacketReceived, false);
 
                                                if (!dPendingPackets.ContainsKey(packetID))
                                                {
                                                        dPendingPackets.Add(packetID, new Dictionary<int, byte[]>());
                                                }
                                                int index = BitConverter.ToInt32(bs, 12);
                                                byte[] packetBuff = new byte[bs.Length - 16];
                                                Array.Copy(bs, 16, packetBuff, 0, packetBuff.Length);
                                                if (dPendingPackets[packetID].ContainsKey(index))
                                                {
                                                        dPendingPackets[packetID][index] = packetBuff;
                                                }
                                                else
                                                {
                                                        dPendingPackets[packetID].Add(index, packetBuff);
                                                }
                                                if (dPendingPackets[packetID].Count == totalSec)
                                                {
                                                        ProcessingPacket(dPendingPackets[packetID], sender);
                                                }
                                        }
                                }
                        }
                        OnStartReceive(s);
                }
        }
 
        private void ProcessingPacket(Dictionary<int, byte[]> dictionary, IPEndPoint ep)
        {
                byte[] buffer = new byte[bufferSize * dictionary.Count];
                int total = 0;
                for (int i = 0; i < dictionary.Count; i++)
                {
                        Array.Copy(dictionary[i], 0, buffer, total, dictionary[i].Length);
                        total += dictionary[i].Length;
                }
                int def = BitConverter.ToInt32(buffer, 0);
                byte[] result = new byte[total - 4];
                Array.Copy(buffer, 4, result, 0, result.Length);
                delReceive(result, ep, def);
        }
 
        Dictionary<int, Dictionary<int, byte[]>> dPendingPackets = new Dictionary<int, Dictionary<int, byte