Avatar billede danieltoft Nybegynder
06. januar 2009 - 09:33 Der er 28 kommentarer og
1 løsning

Domain Name til IP Addresse i C#

Hej alle samme

Jeg er ved at lave et program som henter en masse domaine navne fra en liste og slår derefter IP'en op på de forskellige domains.

Det gør jeg ved at bruge Dns.GetHostEntry(DomainName).

Mit problem er at hvis den ikke kan slå ip addressen op på et domain tager det utroligt lang tid inden den fejler. Det er jo lidt træls når man har en liste på 2000 domaine Navne.
Kan man på en eller anden måde sætte timeout tiden ned?

Håber der er nogle der kan hjælpe :-)

Mvh Daniel Toft
Avatar billede arne_v Ekspert
06. januar 2009 - 09:47 #1
Jeg ville nok satse paa noget multithreaded evt. noget asynch.
Avatar billede Spotgun Seniormester
06. januar 2009 - 11:41 #2
Jeg lavede et lignende program i forbindelse med flytning af en række webservere. Jeg fandt frem til denne komponent, som er både hurtig og nem at have med at gøre, og så er den gratis: http://www.majodio.com/products.aspx?p=Majodio.Dns

Her er et hurtigt eksempel:
                string domain = "www.google.dk";
                Majodio.Dns.DnsRequest req = new DnsRequest(domain);
                Majodio.Dns.DnsResponse res = req.GetDnsResponse();
                if (res.AnswerCount < 1)
                {
                    Console.WriteLine("No matches for " + domain);
                }
                foreach (Answer a in res.Answers)
                {
                    Console.WriteLine(domain + ": " + a.ToString());
                }
Avatar billede arne_v Ekspert
11. januar 2009 - 02:46 #3
Til inspiration:

using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;

namespace E
{
    public class Program
    {
        public class Param
        {
            private string host;
            private Dictionary<string, IPAddress[]> table;
            public Param(string host, Dictionary<string, IPAddress[]> table)
            {
                this.host = host;
                this.table = table;
            }
            public string Host { get { return host; }  }
            public Dictionary<string, IPAddress[]> Table { get { return table; } }
        }
        public static void Main(string[] args)
        {
            List<string> hosts = new List<string>();
            hosts.Add("www.eksperten.dk");
            hosts.Add("www.google.com");
            hosts.Add("www.microsoft.com");
            hosts.Add("www.noexisting.dk");
            Dictionary<string, IPAddress[]> table = new Dictionary<string, IPAddress[]>();
            ThreadPool.SetMaxThreads(100, 100);
            foreach(string host in hosts)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(Lookup), new Param(host, table));
            }
            while(table.Count < hosts.Count)
            {
                Thread.Sleep(100);
            }
            foreach(string host in table.Keys)
            {
                Console.WriteLine(host + ":");
                if(table[host] != null)
                {
                    foreach(IPAddress addr in table[host])
                    {
                        Console.WriteLine("  " + addr.ToString());
                    }
                }
                else
                {
                    Console.WriteLine("  Unknown");
                }
            }
            Console.ReadKey();
        }
        public static void Lookup(object info)
        {
            Param realinfo = (Param)info;
            IPAddress[] addr;
            try
            {
                addr = Dns.GetHostEntry(realinfo.Host).AddressList;
            }
            catch (Exception)
            {
                addr = null;
            }
            lock(realinfo.Table)
            {
                realinfo.Table.Add(realinfo.Host, addr);
            }
        }
    }
}
Avatar billede websmith Nybegynder
11. januar 2009 - 13:58 #4
//Her gemmes resultaterne
        private Dictionary<string, IPAddress[]> hostNames = new Dictionary<string, IPAddress[]>();

        private Dictionary<string, string> exceptions = new Dictionary<string, string>();

        //Request counter til at styre antallet af samtidige requests
        private long requestCounter;
        public void LookupHostNames()
        {
              string[] hostnames=new string[]{"www.eksperten.dk","www.idg.dk"};

            foreach(string hostname in hostnames)
            {
                while(Interlocked.Read(ref requestCounter)>=5)
                {
                    Thread.Sleep(50);
                }
                //Increment request counter, så vi ikke starter for mange threads
                Interlocked.Increment(ref requestCounter);
                Dns.BeginGetHostEntry(hostname, ProcessDnsInformation, hostname);
            }

           
        }

        private void ProcessDnsInformation(IAsyncResult result)
        {
            string hostName = (string) result.AsyncState;
           
            try
            {
                //Hent resultatet
                IPHostEntry host = Dns.EndGetHostEntry(result);
                hostNames.Add(hostName, host.AddressList);
               
            }
            //Gem exception
            catch (SocketException e)
            {
                exceptions.Add(hostName, e.ToString());
               
            }
            finally
            {
                //Decrement antal af requests som kører
                Interlocked.Decrement(ref requestCounter);
            }
        }
Avatar billede websmith Nybegynder
11. januar 2009 - 14:00 #5
Jeg glemte selvfølgelig en while løkke efter min foreach(string hostname)

Ellers vil metoden jo returnere lige så snart foreach løkken er færdig.

Den skal være:

while(Interlocked.Read(ref requestCounter)>0)
{
Thread.Sleep(50);
}

Så vil den vente til alle requests er færdige før metoden returnerer
Avatar billede danieltoft Nybegynder
11. januar 2009 - 16:50 #6
Tak for jeres gode svar. :-)

Jeg har kigget lidt på det arne_v har lavet det er rigtigt godt. Der er bare lige et problem. Når Der er 2 domains der er ens laver den en fejl. Hvordan kan det være?

Mvh Daniel Toft
Avatar billede arne_v Ekspert
11. januar 2009 - 16:53 #7
Fordi key allerede eksisterer i Dictionary.

Det kunne man teste for.
Avatar billede danieltoft Nybegynder
11. januar 2009 - 17:03 #8
Ved du hvordan?
Avatar billede arne_v Ekspert
11. januar 2009 - 17:04 #9
Jeg prøver lige at modificere koden til det.
Avatar billede danieltoft Nybegynder
11. januar 2009 - 17:14 #10
Mange Tak :-)
Avatar billede arne_v Ekspert
11. januar 2009 - 17:16 #11
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;

namespace E
{
    public class Program
    {
        public delegate void IncrementDuplicate();
        public class Param
        {
            private string host;
            private Dictionary<string, IPAddress[]> table;
            private IncrementDuplicate p;
            public Param(string host, Dictionary<string, IPAddress[]> table, IncrementDuplicate p)
            {
                this.host = host;
                this.table = table;
                this.p = p;
            }
            public string Host { get { return host; }  }
            public Dictionary<string, IPAddress[]> Table { get { return table; } }
            public void Duplicate() { p(); }
        }
        private static int duplicate;
        private static object lck = new object();
        public static void Increment()
        {
            lock(lck)
            {
                duplicate++;
            }
        }
        public static void Main(string[] args)
        {
            List<string> hosts = new List<string>();
            hosts.Add("www.eksperten.dk");
            hosts.Add("www.google.com");
            hosts.Add("www.microsoft.com");
            hosts.Add("www.noexisting.dk");
            hosts.Add("www.google.com");
            Dictionary<string, IPAddress[]> table = new Dictionary<string, IPAddress[]>();
            ThreadPool.SetMaxThreads(100, 100);
            foreach(string host in hosts)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(Lookup), new Param(host, table, Increment));
            }
            while(table.Count + duplicate < hosts.Count)
            {
                Thread.Sleep(100);
            }
            foreach(string host in table.Keys)
            {
                Console.WriteLine(host + ":");
                if(table[host] != null)
                {
                    foreach(IPAddress addr in table[host])
                    {
                        Console.WriteLine("  " + addr.ToString());
                    }
                }
                else
                {
                    Console.WriteLine("  Unknown");
                }
            }
            Console.ReadKey();
        }
        public static void Lookup(object info)
        {
            Param realinfo = (Param)info;
            bool already;
            lock(realinfo.Table)
            {
                already = realinfo.Table.ContainsKey(realinfo.Host);   
            }
            if (already)
            {
                realinfo.Duplicate();
            }
            else
            {
                IPAddress[] addr;
                try
                {
                    addr = Dns.GetHostEntry(realinfo.Host).AddressList;
                }
                catch (Exception)
                {
                    addr = null;
                }
                lock(realinfo.Table)
                {
                    realinfo.Table.Add(realinfo.Host, addr);
                }
            }
        }
    }
}
Avatar billede danieltoft Nybegynder
11. januar 2009 - 18:42 #12
Det virker fint når jeg køre min liste igennem med 250 domains. Men når jeg køre min liste med 1500 domains går det galt. Den laver fejl på det her:

lock(realinfo.Table)
{
      realinfo.Table.Add(realinfo.Host, addr);
}

Fejlbesked:
An item with the same key has already been added.

Har du en ide om hvad det kan være?

Mvh Daniel Toft
Avatar billede arne_v Ekspert
11. januar 2009 - 18:46 #13
Hm. Jeg lavede et fuckup i testet. Jeg prøver lige igen.
Avatar billede arne_v Ekspert
11. januar 2009 - 18:51 #14
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;

namespace E
{
    public class Program
    {
        public delegate void IncrementDuplicate();
        public class Param
        {
            private string host;
            private Dictionary<string, IPAddress[]> table;
            private IncrementDuplicate p;
            public Param(string host, Dictionary<string, IPAddress[]> table, IncrementDuplicate p)
            {
                this.host = host;
                this.table = table;
                this.p = p;
            }
            public string Host { get { return host; }  }
            public Dictionary<string, IPAddress[]> Table { get { return table; } }
            public void Duplicate() { p(); }
        }
        private static int duplicate;
        private static object lck = new object();
        public static void Increment()
        {
            lock(lck)
            {
                duplicate++;
            }
        }
        public static void Main(string[] args)
        {
            List<string> hosts = new List<string>();
            hosts.Add("www.eksperten.dk");
            hosts.Add("www.google.com");
            hosts.Add("www.microsoft.com");
            hosts.Add("www.noexisting.dk");
            hosts.Add("www.google.com");
            Dictionary<string, IPAddress[]> table = new Dictionary<string, IPAddress[]>();
            ThreadPool.SetMaxThreads(100, 100);
            foreach(string host in hosts)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(Lookup), new Param(host, table, Increment));
            }
            while(table.Count + duplicate < hosts.Count)
            {
                Thread.Sleep(100);
            }
            foreach(string host in table.Keys)
            {
                Console.WriteLine(host + ":");
                if(table[host] != null)
                {
                    foreach(IPAddress addr in table[host])
                    {
                        Console.WriteLine("  " + addr.ToString());
                    }
                }
                else
                {
                    Console.WriteLine("  Unknown");
                }
            }
            Console.ReadKey();
        }
        public static void Lookup(object info)
        {
            Param realinfo = (Param)info;
            bool already;
            lock(realinfo.Table)
            {
                already = realinfo.Table.ContainsKey(realinfo.Host); 
            }
            if(already)
            {
                realinfo.Duplicate();
            }
            else
            {
                IPAddress[] addr;
                try
                {
                    addr = Dns.GetHostEntry(realinfo.Host).AddressList;
                }
                catch (Exception)
                {
                    addr = null;
                }
                lock(realinfo.Table)
                {
                    if (realinfo.Table.ContainsKey(realinfo.Host))
                    {
                        realinfo.Duplicate();
                    }
                    else
                    {
                        realinfo.Table.Add(realinfo.Host, addr);
                    }
                }
            }
        }
    }
}
Avatar billede danieltoft Nybegynder
11. januar 2009 - 19:04 #15
Nu står den bare og hænger. Den kommer aldrig videre.
Avatar billede danieltoft Nybegynder
11. januar 2009 - 19:07 #16
Joo min fejl. Det virker nu :-). Mange tak for hjælpen. Smid et svar så jeg kan give dig point.
Avatar billede arne_v Ekspert
11. januar 2009 - 19:33 #17
svar
Avatar billede websmith Nybegynder
11. januar 2009 - 19:49 #18
Lige en kommentar inden dette her bliver lukket.

Selv om arne's løsning fungerer som den skal, så er den unødvendig kompleks.

Fx behøver man ikke at bruge locking til at inkrementere en simpel værdi.

Det er Interlocked.Increment opfundet til.

Og derudover, da Dns klassen har asynkrone metoder til at lave dns opslag, så virker det lidt som at gå over åen for at hente vand at bruge en thread pool for at gøre dns opslag asynkront.

Sidste kommentar er at hvis du starter 200 threads op, som den thread pool vil gørel, så vil dit program kommer til at bruge ret meget hukommelse, da hver thread tager minimum 1MB hukommelse.

Men som sagt, koden fungerer. Jeg ville bare lige påpege et par forbedringer, for eksperten er vel også om at gøre folk bedre til at programmere, ikke kun give løsninger på problemer :)
Avatar billede arne_v Ekspert
11. januar 2009 - 20:14 #19
Jeg kender godt Interlocked.Increment, men der er to problemer som skal løses:
- en anden tråd må ikke læse skrive imens
- værdien skal være visible for andre tråde

Det fremgår tydeligt af dokumentationen at Interlocked.Increment løser det første
problem. Jeg er ret sikker på at implementationen af Interlocked.Increment også vil sikre
at det andet problem er løst. Men jeg i mangel på garanti valgte jeg lock, fordi
den garanterer at begge problemer er løst.
Avatar billede arne_v Ekspert
11. januar 2009 - 20:23 #20
Jeg nævnte asynch i det aller første indlæg, men generelt kan jeg ikke lide .NET's
asynch metoder.

Koden bruger en del memory. Men jeg gætter på at spørger har 200 MB til programmet.
Avatar billede websmith Nybegynder
11. januar 2009 - 21:28 #21
Hvorfor må en anden tråd ikke skrive imens?

Det er bare en counter du har lavet som beskriver hvor mange duplicates der er, så dit regnskab i din while(table.Count + duplicate < hosts.Count) går op.

I øvrigt tilgår du duplicate variablen direkte, hvilket modsiger din udtalelse om at andre threads ikke må læse. Hvis du skulle have lavet det korrekt, så ville du have lavet en ReadDuplicates metode som også havde lavet en lock.

Interlocked forhindrer ikke at ændre tråde kan læse, og der er skam også en Interlocked.Read som sikrer at man ikke får en halv værdi læst ud.

Problemet med at bruge:

x++ er at det rent faktisk er to instruktioner, læs x, gør x større. imellem de to operationer kan der ske noget som gør resultatet forkert.
Interlocked.Increment sørger for at der ikke er andre tråde som kan tilgå variablen imens den bliver inkrementeret.


hvis du havde brugt Interlocked.Read og interlocked.Increment havde du sluppet for dine locks i forbindelse med inkremtering af duplicates variablen.

Men som jeg skrev i min kommentar, så fungerer koden og det er jo det vigtigste.

Og med hensyn til memory forbrug, så har du sikkert ret, men derudover så er 200 threads ret meget at spawne på en gang.
Avatar billede danieltoft Nybegynder
11. januar 2009 - 21:49 #22
Vil du ikke prøve at skrive et eksempel på hvordan du vil have lavet det.
Avatar billede arne_v Ekspert
11. januar 2009 - 21:49 #23
Du har ret - der er brug for lock i main thread også.

using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;

namespace E
{
    public class Program
    {
        public delegate void IncrementDuplicate();
        public class Param
        {
            private string host;
            private Dictionary<string, IPAddress[]> table;
            private IncrementDuplicate p;
            public Param(string host, Dictionary<string, IPAddress[]> table, IncrementDuplicate p)
            {
                this.host = host;
                this.table = table;
                this.p = p;
            }
            public string Host { get { return host; }  }
            public Dictionary<string, IPAddress[]> Table { get { return table; } }
            public void Duplicate() { p(); }
        }
        private static int duplicate;
        private static object lck = new object();
        public static void Increment()
        {
            lock(lck)
            {
                duplicate++;
            }
        }
        public static int GetDuplicate()
        {
            int res;
            lock(lck)
            {
                res = duplicate;
            }
            return res;
        }
        public static void Main(string[] args)
        {
            List<string> hosts = new List<string>();
            hosts.Add("www.eksperten.dk");
            hosts.Add("www.google.com");
            hosts.Add("www.microsoft.com");
            hosts.Add("www.noexisting.dk");
            hosts.Add("www.google.com");
            Dictionary<string, IPAddress[]> table = new Dictionary<string, IPAddress[]>();
            ThreadPool.SetMaxThreads(100, 100);
            foreach(string host in hosts)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(Lookup), new Param(host, table, Increment));
            }
            while(table.Count + GetDuplicate() < hosts.Count)
            {
                Thread.Sleep(100);
            }
            foreach(string host in table.Keys)
            {
                Console.WriteLine(host + ":");
                if(table[host] != null)
                {
                    foreach(IPAddress addr in table[host])
                    {
                        Console.WriteLine("  " + addr.ToString());
                    }
                }
                else
                {
                    Console.WriteLine("  Unknown");
                }
            }
            Console.ReadKey();
        }
        public static void Lookup(object info)
        {
            Param realinfo = (Param)info;
            bool already;
            lock(realinfo.Table)
            {
                already = realinfo.Table.ContainsKey(realinfo.Host); 
            }
            if(already)
            {
                realinfo.Duplicate();
            }
            else
            {
                IPAddress[] addr;
                try
                {
                    addr = Dns.GetHostEntry(realinfo.Host).AddressList;
                }
                catch (Exception)
                {
                    addr = null;
                }
                lock(realinfo.Table)
                {
                    if (realinfo.Table.ContainsKey(realinfo.Host))
                    {
                        realinfo.Duplicate();
                    }
                    else
                    {
                        realinfo.Table.Add(realinfo.Host, addr);
                    }
                }
            }
        }
    }
}
Avatar billede websmith Nybegynder
11. januar 2009 - 21:53 #24
Jeg lavede et eksempel, men jeg kan da godt lave et mere udførligt, som også tager højde for duplicates.
Giv mig nogle minutter, så skulle det være klar :)
Avatar billede danieltoft Nybegynder
11. januar 2009 - 22:05 #25
Okay det kunne være dejligt. Jeg er nemlig stadig lidt af en nybegynder :P.
Avatar billede websmith Nybegynder
11. januar 2009 - 22:33 #26
Først kommer koden som viser hvordan man bruger koden :)

static void Main(string[] args)
        {

            DnsResolver dr = new DnsResolver(5);
            Dictionary<string, IPAddress[]> addresses = dr.Resolve("www.idg.dk", "www.eksperten.dk", null, "pcworld.dk",
                                                                  "www.google.com", "www.idg.dk", "www.eksperten.dk");


            foreach (string key in addresses.Keys)
            {
                StringBuilder sb = new StringBuilder(100);

                IPAddress[] resolved = addresses[key];
                if (resolved != null)
                {
                    sb.Append(string.Format("{0} resolved into {1} ip addresses(s)\r\n", key, addresses[key].Length));

                    foreach (IPAddress adr in addresses[key])
                    {
                        sb.Append(string.Format("\t{0}", adr));
                    }
                }
                else
                {
                    sb.Append(string.Format("{0} could not be resolved\r\n", key));
                }
                Console.WriteLine(sb.ToString());
            }

            Console.ReadKey();
        }
Avatar billede websmith Nybegynder
11. januar 2009 - 22:33 #27
Og så koden:

    /// <summary>
    /// Class that simplifies DNS resolution
    /// </summary>
    public class DnsResolver
    {
        /// <summary>
        /// Default number of threads, override by using constructor
        /// </summary>
        int maxNumberOfThreads = 5;

        //Counter to hold the number of currently processing threads
        long requestCounter;

        //Counter to hold the number of hostnames queued for processing
        long queuedHostnames;

        //Counter to hold the number of hostnames queued in total
        private long numberOfHostNames;

        //Create dictionary with space for 50 elements, to avoid expanding so many times
        private Dictionary<string, IPAddress[]> resolved = new Dictionary<string, IPAddress[]>(50);

        //Semaphore to make sure only one thread read and write to the dictionary of resolved hostnames
        private Semaphore locker = new Semaphore(0, 1);

        public DnsResolver()
        {
            //Nothing to do here
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DnsResolver"/> class.
        /// </summary>
        /// <param name="numberOfThreads">The number of threads.</param>
        public DnsResolver(int numberOfThreads)
        {
            maxNumberOfThreads = numberOfThreads;   
        }

       

        /// <summary>
        /// Resolves the specified hostnames into their respective IPAddresses
        /// </summary>
        /// <param name="hostnames">The hostnames.</param>
        /// <returns></returns>
        public Dictionary<string,IPAddress[]> Resolve(params string[] hostnames)
        {
            List<string> hosts = new List<string>(hostnames);
            return Resolve(hosts);
        }

        /// <summary>
        /// Resolves the specified hostnames into their respective IPAddresses
        /// </summary>
        /// <param name="hostnames">The hostnames.</param>
        /// <returns></returns>
        public Dictionary<string,IPAddress[]> Resolve(List<string> hostnames)
        {
            //Release semaphore so threads can access the dictionary
            locker.Release(1);

            numberOfHostNames = hostnames.Count;

            foreach (string hostname in hostnames)
            {
                while (Interlocked.Read(ref requestCounter) >= maxNumberOfThreads)
                {
                    Thread.Sleep(50);
                }
                //Check to make sure we dont lookup empty or null
                if(string.IsNullOrEmpty(hostname))
                {
                    //Decrement number of hostnames
                    numberOfHostNames--;
                    continue;
                }
                //Increment request counter
                Interlocked.Increment(ref requestCounter);

                //Increment the number of hosts queued for dns resolution
                Interlocked.Increment(ref queuedHostnames);
               
                //starts the dns resolution
                Dns.BeginGetHostEntry(hostname, ProcessDnsInformation, hostname);
               
            }

            while(!AllDone)
            {
                //Sleep 50 ms, while threads complete
                Thread.Sleep(50);
            }
            return resolved;
        }

        /// <summary>
        /// Gets a value indicating whether all processing has been done or not
        /// </summary>
        private bool AllDone
        {
            get
            {
                bool noThreadsExecuting = Interlocked.Read(ref requestCounter) == 0;
                bool allQueued = Interlocked.Read(ref queuedHostnames) == numberOfHostNames;
                return allQueued && noThreadsExecuting;
            }
        }

        /// <summary>
        /// Adds the hostname and the IPAddresses to the resolved dictionary if the hostname does not alreay
        /// exist in the the dictionary
        /// </summary>
        /// <param name="hostName">Name of the host.</param>
        /// <param name="addresses">The addresses.</param>
        private void AddIfUnique(string hostName, IPAddress[] addresses)
        {
            locker.WaitOne();
            try
            {
                if(!resolved.ContainsKey(hostName))
                {
                    resolved.Add(hostName, addresses);
                }
            }
            finally
            {
                locker.Release();
            }
        }

        /// <summary>
        /// Checks whether or not the hostname has already been resolved
        /// </summary>
        /// <param name="hostName">Name of the host.</param>
        private bool Exists(string hostName)
        {
            locker.WaitOne();
            bool exist = resolved.ContainsKey(hostName);
            locker.Release();
            return exist;
        }

        /// <summary>
        /// Processes the DNS information.
        /// </summary>
        /// <param name="result">The result.</param>
        private void ProcessDnsInformation(IAsyncResult result)
        {
            string hostName = (string)result.AsyncState;


            try
            {
                //Extra check to avoid the actual DNS lookup if we have already done the dns lookup
                if (Exists(hostName))
                {
                    return;
                }

                //Hent resultatet
                IPHostEntry host = Dns.EndGetHostEntry(result);
                AddIfUnique(hostName, host.AddressList);

            }
           
            catch (Exception e)
            {
                //We could log the exception or have a property with the exceptions occured
                AddIfUnique(hostName, null);
            }
            finally
            {
                //Decrement number of request processing
                Interlocked.Decrement(ref requestCounter);
            }
        }
    }
Avatar billede websmith Nybegynder
11. januar 2009 - 22:37 #28
Det blev lidt mere udførligt end først tiltænkt, men jeg tænkte at et nemt interface var at foretrække.

Og der er sikkert stadigvæk muligheder for forbedringer :)

Men som sagt, arne's kode fungerer, og det behøver ikke være omstændigt eller stort for at få løst problemet.

Hvis man skulle gøre det rigtig fint, så ville jeg nok have lavet metoden Resolve asynkron, og poste et event når en adresse blev resolved, men det er lidt out of scope her.
Avatar billede websmith Nybegynder
11. januar 2009 - 22:38 #29
Min brug af semphorer er nok overkill, men alligevel :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester