/* * Copyright (c) 2005-2006 Erik Tigerholm * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ using System; using System.Text; using System.Net; using System.Net.Sockets; using System.Collections; using System.Collections.Generic; namespace OpenTraffic.Network { class UdpClient : IDisposable { public const int ACK = -1; public interface PacketFactory { Packet CreatePacket(int t, T obj, UdpClient n, IPEndPoint ip, int seq); } public int port; private T obj; private Queue packetList; private Dictionary>> sendPacket; private Socket socket; // private int lastSequenceSent; private Dictionary lastSequenceSent; private Dictionary> activePacket; private Dictionary lastSequenceRecieved; private PacketFactory packetFactory; private int totSent; private int packetWaitingAck; private long timer; EndPoint recvIp; public UdpClient(int port, PacketFactory pack, T obj) { this.obj = obj; this.port = port; packetFactory = pack; lastSequenceSent = new Dictionary(); lastSequenceRecieved = new Dictionary(); activePacket = new Dictionary>(); packetList = new Queue(); sendPacket = new Dictionary>>(); this.port = port; socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.Bind(new IPEndPoint(IPAddress.Any, port)); socket.ReceiveBufferSize = 65536; socket.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.NoChecksum, false); bs = new byte[65535]; recvIp = new IPEndPoint(IPAddress.Any, this.port); } public void Dispose() { socket.Close(); } public int GetSequence(IPEndPoint ip) { if (!lastSequenceSent.ContainsKey(ip)) { lastSequenceSent.Add(ip, 10); } lastSequenceSent[ip] = lastSequenceSent[ip] + 1; return lastSequenceSent[ip]; } public bool Ready { get { foreach (KeyValuePair>> kv in sendPacket) { if (kv.Value.Count != 0) return false; } return packetList.Count == 0; } } public void AddPacketToSend(Packet p) { packetList.Enqueue(p); } public void AckPacket(IPEndPoint ipe, int seq) { packetWaitingAck--; sendPacket[ipe].Remove(seq); } byte[] bs; private void SendPacket(Packet p) { // prof.Start("fill in seq"); if (p.Sequence == 0) { p.Sequence = GetSequence(p.ip); /*if (!sendPacket.ContainsKey(p.ip)) sendPacket.Add(p.ip, new Dictionary>()); sendPacket[p.ip].Add(p.Sequence, p); */ Dictionary> dict = null; if (!sendPacket.TryGetValue(p.ip, out dict)) { dict = new Dictionary>(); sendPacket.Add(p.ip, dict); } packetWaitingAck++; dict.Add(p.Sequence, p); } // prof.SStart("reset timer"); timer = Environment.TickCount + 5 * 1000 * 60; p.resetTimer(timer); bool cont = true; //prof.SStart("send packet"); // Console.WriteLine(p); for (int packet = 0; cont; packet++) { int packetSize = 0; // prof.Start("serialize"); cont = p.serialize(bs.Length, packet, bs, out packetSize); // prof.SStart("send packet"); socket.SendTo(bs, 0, packetSize, SocketFlags.None, p.ip); // prof.Stop(); totSent += packetSize; // } //prof.Stop(); //Console.WriteLine(p + "=> " + pSize); } public override string ToString() { return "listening on " + this.port + " and have received " + totSent + " bytes"; } public void Reset() { totSent = 0; this.sendPacket.Clear(); packetWaitingAck = 0; packetList.Clear(); } public void go() { // prof.Start("send"); while (packetList.Count > 0) { Packet packet = (Packet)packetList.Dequeue(); this.SendPacket(packet); } // prof.SStart("recv"); if (socket.Available != 0) { // prof.Start("init"); // prof.Start("recv"); int size = socket.ReceiveFrom(bs, ref recvIp); // prof.SStart("fill in"); IPEndPoint ipe = recvIp as IPEndPoint; int sequence = Packet.GetSequence(bs); int type = Packet.GetType(bs); //prof.Stop(); if (type == ACK) { // prof.SStart("AckPacket"); Packet p = this.CreatePacket(bs, ipe); p.DoAction(); } else { // prof.SStart("norm pack"); if (!activePacket.ContainsKey(ipe)) { if (!lastSequenceRecieved.ContainsKey(ipe)) { lastSequenceRecieved.Add(ipe, sequence); } if (sequence == lastSequenceRecieved[ipe]) { Packet p = CreatePacket(bs, ipe); p.AddData(bs, size); if (p.HasRecievedAllPacket()) { p.Deserialize(); p.DoAction(); } else { activePacket.Add(ipe, p); } // Console.WriteLine(port + "<---" + activePacket[ipe]); lastSequenceRecieved[ipe] = lastSequenceRecieved[ipe] + 1; } else { Console.WriteLine("Skipped packet because packet is outside the window: " + lastSequenceRecieved[ipe] + " sequence: " + sequence); } } else { if (activePacket[ipe].Sequence == sequence) { // Console.WriteLine(port + "Waiting por bytes " + activePacket[ipe].bytesLeft + // " with sequence " + activePacket[ipe].Sequence); activePacket[ipe].AddData(bs, size); } if (activePacket[ipe].HasRecievedAllPacket()) { activePacket[ipe].Deserialize(); activePacket[ipe].DoAction(); // Console.WriteLine(port + "pack recieved" + activePacket[ipe]); activePacket.Remove(ipe); } } } // prof.Stop(); } // prof.SStart("checkresend"); if (packetWaitingAck != 0) { if (timer < Environment.TickCount) { foreach (KeyValuePair>> kv2 in sendPacket) { foreach (KeyValuePair> kv in kv2.Value) { Packet p2 = kv.Value; // prof.Start("packet"); if (p2.CheckResend()) { Console.WriteLine(port + "---> Trying to resend: " + p2); this.SendPacket(p2); } // prof.Stop(); } } } } // prof.Stop(); } public void Ping(int port1) { byte[] data = new byte[256]; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); IPEndPoint remoteEP = new IPEndPoint(IPAddress.Broadcast, port1); socket.SendTo(data, remoteEP); } public Packet CreatePacket(byte[] bytes, IPEndPoint ip) { int type = Packet.GetType(bytes); int seq = Packet.GetSequence(bytes); ; int bytesLeft = BitConverter.ToInt32(bytes, 8); // int offset = BitConverter.ToInt32(bytes, 12); Packet packet = null; packet = packetFactory.CreatePacket(type, obj, this, ip, seq); packet.data = new Byte[bytesLeft]; packet.bytesLeft = bytesLeft; packet.Sequence = seq; return packet; } } }