Avatar billede telep Nybegynder
29. august 2005 - 10:02 Der er 26 kommentarer og
1 løsning

Sockets: Send og receive i et hug

Hejsa,

Jeg er igang med noget socketprogrammering. Det drejer sig om en SIP klient til ip-telefoni... men det er sådan set ikke væsentligt.

Jeg skal sende en besked til min SIP server (går fint lige nu), problmerne opstår når jeg skal modtage noget fra SIP serveren....

Når jeg sender en besked til SIP serveren svarer den tilbage at den har modtaget min besked. Den besked kan jeg ikke få mit program til at fange...

Lidt kode:

using System;
using System.Text;
using System.IO;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace tmSock
{
    /// <summary>
    ///
    /// </summary>
    public class sock
    {
        public sock()
        {
            //
            // TODO: Add constructor logic here
            //
        }
   
        private static Socket ConnectSocket(string server, int port)
        {
            Socket s = null;
            IPHostEntry hostEntry = null;
       
            // Get host related information.
            hostEntry = Dns.Resolve(server);

            // Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
            // an exception that occurs when the host IP Address is not compatible with the address family
            // (typical in the IPv6 case).
            foreach(IPAddress address in hostEntry.AddressList)
            {
                IPEndPoint ipe = new IPEndPoint(address, port);
                Socket tempSocket =
                    new Socket(ipe.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

                    s = tempSocket;
                    break;
            }
            return s;
        }
   
        private static string SocketSendReceive(string server, int port)
        {
            string request = "INVITE sip:user2@cm01dk SIP/2.0\n" +
                            "Via: SIP/2.0/UDP cm01dk:5060;branch=z9hG4bLfw19b\n" +
                            "Max-Forwards: 70\n" +
                            "To: J.Eg <user2@cm01dk>\n" +
                            "From: tmdell <g4tm@cm01dk>;tag=1234\n" +
                            "Call-ID: 12341235@cm01dk\n"  +
                            "CSeq: 1 INVITE\n" +
                            "Subject: test, test\n" +
                            "Contact: <sip:g4tm@cm01dk>\n" +
                            "Content-Type: application/sdp\n" +
                            "Content-Length: 158\n\n";

            Byte[] bytesSent = Encoding.UTF8.GetBytes(request);
            Byte[] bytesReceived = new Byte[2048];
     
            Create a socket connection with the specified server and port.
            Socket s = ConnectSocket(server, port);

            if (s == null)
                return ("Connection failed");
       
            // Send request to the server.
           
            IPHostEntry hostEntry = Dns.Resolve(server);
            IPAddress address = hostEntry.AddressList[0];
            IPEndPoint ipe = new IPEndPoint(address, port);
           
            s.SendTo(bytesSent, ipe);
           
            s.Close();
            // Receive the server home page content.
           
            int bytes = 0;
            string page = "";
           
            //do
            //{   
                //bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
               
                bytesReceived = receiveForFanden();
               
                page = page + Encoding.UTF8.GetString(bytesReceived, 0, bytes);
            //}
            //while (bytes > 0);

            return page;
        }

        private static byte[] receiveForFanden()
        {
            IPHostEntry hostEntry = Dns.Resolve("cm01dk");
            IPEndPoint endPoint = new IPEndPoint(hostEntry.AddressList[0], 5060);

            Socket c = new Socket(endPoint.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
       
            // Creates an IpEndPoint to capture the identity of the sending host.
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint senderRemote = (EndPoint)sender;
   
            // Binding is required with ReceiveFrom calls.
            c.Bind(endPoint);
            byte[] msg = new Byte[2048];
           
            // This call blocks. 
            c.ReceiveFrom(msg, 0, msg.Length, SocketFlags.None, ref senderRemote);
            c.Close();
            return msg;
        }

        public string sockStart(string host_, int port_)
        {
            string host = host_;
            int port = port_;

            string result = SocketSendReceive(host, port);
            return result;
        }

    }
}
Avatar billede telep Nybegynder
29. august 2005 - 10:07 #1
er yderligere forklaring nødvendig?

Jeg skal sådan set bare ha' et eksempel på hvordan jeg sender og modtager i et hug...

altså:

socket.sendTo(detDerSkalSendes, param2, param3)
socket.receiveFrom(modtagerBuffer, param2, param3)

eller hvordan det nu lige er...

På forhånd tak!

/troels
Avatar billede dsj Nybegynder
29. august 2005 - 10:17 #2
Efter du har sendt beskeden afsted lukker du forbindelsen med s.Close(), hvilket er årsagen til at du ikke modtager noget respons. Hvis du fjerner s.Close() skulle det virke lidt bedre.
Avatar billede anadan Nybegynder
29. august 2005 - 10:18 #3
Jeg kan ikke helt forstå hvorfor du lukker din forbindelse efter at du har afsendt data, og så åbner en ny for at modtage...
Avatar billede anadan Nybegynder
29. august 2005 - 10:20 #4
Jeg vil også anbefale at ændre din modtagelses buffer (page) til en StringBuilder

StringBuilder page = new StringBuilder();
.
.
page.Append(Encoding.UTF8.GetString(bytesReceived, 0, bytes));
.
.
return page.ToString();
Avatar billede telep Nybegynder
29. august 2005 - 10:27 #5
Det var egentlig heller ikke meningen at der skulle være s.Close() med... blot en lille test på programmet fra min side, da jeg fik følgende fejl meddl.

An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in system.dll

Additional information: Adressen, der blev anmodet om, er ikke gyldig i sin kontekst

Denne fejl opstår også nu (jeg har fjernet s.Close()) programmet stopper når der bliver sagt c.Bind(endPoint)

any clues?
Avatar billede anadan Nybegynder
29. august 2005 - 10:55 #6
Hmm, det lader til at du har lånt oget af koden fra

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnetsocketssocketclasstopic.asp

Men der er enkelte dele der mangler, blandt andet i ConnectSocket metoden mangler tempSocket.Connect()
Avatar billede telep Nybegynder
29. august 2005 - 13:22 #7
ja jeg har lånt derfra - men kun i begrænset omfang. Jeg SKAL bruge UDP og ikke TCP som eksemplet... når man bruger UDP kan man ikke connect'e socket'en da protokollen jo er connectionless...
Avatar billede anadan Nybegynder
29. august 2005 - 13:29 #8
Hmm, hvordan ved du at serveren sender et svar tilbage, når dit program ikke kan fange det? Der er jo ikke garanti for at beskeden når frem med UDP.
Avatar billede telep Nybegynder
29. august 2005 - 13:30 #9
jeg har et sniffer program - og det er 110% sikkert at serveren sender svar - det skal den gøre (det er en kommercielt SIP server, der er til at stole på ;-)

Desuden går programmet jo ned - så der er noget galt i min kode!
Avatar billede arne_v Ekspert
29. august 2005 - 20:18 #10
du kalder bind med et endpoint du slår op via DNS

bind skal kaldes med den lokale server som endpoint

så medmindre dit program kører på "cm01dk" så skal det give fejl
Avatar billede arne_v Ekspert
29. august 2005 - 20:18 #11
iøvrigt ville jeg have lytte klar inden send for ikke at risikere at svaret
gik tabt inden du begynder at lytte
Avatar billede telep Nybegynder
29. august 2005 - 21:11 #12
arne_v:
hvad foreslår du jeg gør?

Jeg har selv tænkt tanken med at lytte før send - samt begyndt at tænke lidt i tråde...
Avatar billede arne_v Ekspert
29. august 2005 - 21:13 #13
du starter med at fixe det endpoint problem
Avatar billede arne_v Ekspert
29. august 2005 - 21:13 #14
og så plejer det at være godt med en lytte tråd
Avatar billede telep Nybegynder
29. august 2005 - 21:19 #15
ok - tak for det.

Kan du komme med et par hints til hvordan jeg fixer endpoint problemet???
Har kigget på det i et par timer nu, men er gået lidt kold...
Avatar billede arne_v Ekspert
29. august 2005 - 21:21 #16
du skal vel bare bruge den lokale adresse fremfor den remote adresse
Avatar billede telep Nybegynder
29. august 2005 - 21:23 #17
så forstår jeg nok ikke helt hvad bind() skal til for... men jeg kigger på det. Tak for hjælpen!

Alle der ønsker points - svarer!
Avatar billede arne_v Ekspert
29. august 2005 - 21:38 #18
fra docs:

Remarks
Use the Bind method if you need to use a specific local endpoint. You must call Bind before you can call the Listen method. You do not need to call Bind before using the Connect method unless you need to use a specific local endpoint. You can use the Bind method on both connectionless and connection-oriented protocols.

Before calling Bind, you must first create the local IPEndPoint from which you intend to communicate data. If you do not care which local address is assigned, you can create an IPEndPoint using IPAddress.Any as the address parameter, and the underlying service provider will assign the most appropriate network address. This might help simplify your application if you have multiple network interfaces. If you do not care which local port is used, you can create an IPEndPoint using 0 for the port number. In this case, the service provider will assign an available port number between 1024 and 5000.

If you use the above approach, you can discover what local network address and port number has been assigned by calling the LocalEndPoint. If you are using a connection-oriented protocol, LocalEndPoint will not return the locally assigned network address until after you have made a call to the Connect or EndConnect method. If you are using a connectionless protocol, you will not have access to this information until you have completed a send or receive.

Note  If you intend to receive multicasted datagrams, you must call the Bind method with a multicast port number.
Note  You must call the Bind method if you intend to receive connectionless datagrams using the ReceiveFrom method.
Note  If you receive a SocketException when calling the Bind method, use SocketException.ErrorCode to obtain the specific error code. Once you have obtained this code, you can refer to the Windows Socket Version 2 API error code documentation in MSDN for a detailed description of the error.
Avatar billede arne_v Ekspert
29. august 2005 - 21:39 #19
og et svar
Avatar billede telep Nybegynder
30. august 2005 - 10:09 #20
Hej igen,

jeg blev ikke helt færdigt med mit problem.

Jeg har nu følgende funktion:

        private static string SocketSendReceive(string server, int port)
        {
            string request = "INVITE sip:user2@cm01dk SIP/2.0\n" +
                            "Via: SIP/2.0/UDP cm01dk:5060;branch=z9hV4bLfw19b\n" +
                            "Max-Forwards: 70\n" +
                            "To: J.Eg <user2@cm01dk>\n" +
                            "From: tmdell <g4tm@cm01dk>;tag=1634\n" +
                            "Call-ID: 12341235@cm01dk\n"  +
                            "CSeq: 1 INVITE\n" +
                            "Subject: test, test\n" +
                            "Contact: <sip:g4tm@cm01dk>\n" +
                            "Content-Type: application/sdp\n" +
                            "Content-Length: 158\r\n\r\n";

            Byte[] bytesSent = Encoding.UTF8.GetBytes(request);
            Byte[] bytesReceived = new Byte[2048];

            //Create a socket connection with the specified server and port.

            IPHostEntry hostEntry = Dns.Resolve(server);
            IPAddress address = hostEntry.AddressList[0];
            IPEndPoint ipe = new IPEndPoint(address, port);

            Socket s = new Socket(ipe.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            if (s == null)
                return ("Connection failed");
       
            // Send request to the server.           
            s.SendTo(bytesSent, ipe);
           
            int bytes = 0;
            string page = "";

            // Creates an IpEndPoint to capture the identity of the sending host.
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint senderRemote = (EndPoint)sender;
           
            IPEndPoint denneMaskine = new IPEndPoint(IPAddress.Any, 0);

            s.Bind(denneMaskine);
            s.ReceiveFrom(bytesReceived, 0, bytesReceived.Length, SocketFlags.None, ref senderRemote);
            page = page + Encoding.UTF8.GetString(bytesReceived, 0, bytes);
       
            return page;
Avatar billede telep Nybegynder
30. august 2005 - 10:11 #21
s.Bind(denneMaskine) giver runtime fejl:
------------------------------------
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in system.dll

Additional information: Der blev angivet et ugyldigt argument
------------------------------------

Hvad fa.... gør jeg galt?
Avatar billede telep Nybegynder
30. august 2005 - 10:50 #22
arne_v: der er selvfølgelig flere points hvis vi får det til at spille!
Avatar billede arne_v Ekspert
30. august 2005 - 15:31 #23
svært at sige

            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            IPEndPoint denneMaskine = new IPEndPoint(IPAddress.Any, 0);
            s.Bind(denneMaskine);

virker fint hos mig

noget helt andet - hvordan skal serveren vide hvilken port du lytter på
når du først finder port nummeret efter at du har sendt til serveren ??
Avatar billede telep Nybegynder
30. august 2005 - 15:41 #24
Det virker også hos mig nu...

Jeg blev dog nødt til at lave to sockets - jeg kunne simpelthen ikke få den socket jeg havde sendt med til at modtage også... mere om det senere...

med denne kode:

        private static string SocketSendReceive(string server, int port)
        {
            string request = "INVITE sip:user6@cm01dk SIP/2.0\r\n" +
                            "Via: SIP/2.0/UDP cm01dk:5060;branch=z9hV4bLfw19b\r\n" +
                            "Max-Forwards: 70\r\n" +
                            "To: t.munch <user6@cm01dk>\r\n" +
                            "From: pikken <g4tm@cm01dk>;tag=1614\r\n" +
                            "Call-ID: 12341235@cm01dk\r\n"  +
                            "CSeq: 1 INVITE\r\n" +
                            "Subject: test, test\r\n" +
                            "Contact: <sip:g4tm@cm01dk>\r\n" +
                            "Content-Type: application/sdp\r\n" +
                            "Content-Length: 158\r\n\r\n";

            Byte[] bytesSent = Encoding.UTF8.GetBytes(request);
            Byte[] bytesReceived = new Byte[2048];

            //Create a socket connection with the specified server and port.
           
            IPHostEntry hostEntry = Dns.Resolve("cm01dk");
            IPAddress address = hostEntry.AddressList[0];
            IPEndPoint ipe = new IPEndPoint(address, port);
           
            Socket s = new Socket(ipe.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            if (s == null)
                return ("Connection failed");
           
            // Send request to the server.           
            s.SendTo(bytesSent, ipe);
            s.Close();

            //int bytes = 0;
            string page = "tm";
           
            //Creates an IpEndPoint to capture the identity of the sending host.
           
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, port);
            EndPoint senderRemote = (EndPoint)sender;

            Socket c = new Socket(sender.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            c.Bind(sender);
           
            bytes = c.ReceiveFrom(bytesReceived, 0, bytesReceived.Length, SocketFlags.None, ref senderRemote); //this blocks
           
            page = Encoding.UTF8.GetString(bytesReceived, 0, bytes);
            c.Close();
           
            return page;
        }

Blot det problem, at serveren sender to beskeder tilbage, men jeg fanger kun den sidste. De kommer på samme portnr.

Jeg tror problemet er, at der ganske enkelt når at komme den første besked før min socket "c" er klar til at tage imod...

Jeg har testet det ved at lave et lille konsol program der blot står lytter, det starter jeg før jeg sender - det program kan godt modtage den første besked...

Men jeg synes det er lidt mærkeligt at det kan gå så stærk at 5 liniers kode ikke kan nå at følge med...
Måske en løsning kunne være at få kogt programmet ned til kun at bruge den selv samme socket???
Avatar billede telep Nybegynder
31. august 2005 - 15:03 #25
Kan jeg forvente mere her?
Avatar billede arne_v Ekspert
31. august 2005 - 16:07 #26
ja - hvad er spørgsmålet ?
Avatar billede telep Nybegynder
31. august 2005 - 21:54 #27
Jeg er gået over til UdpClient - nemmere for mig lige nu, bliver dog nok nødt til at vende tilbage til en "rigtig" socket igen... men den tid den sorg...

Jeg har ikke brug for yderligere her for nuværende.

Men ellers tak - og tak i det hele taget!
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