Julie
Julie

Reputation: 43

UDP Streaming transfer from C# application to PHP webpage

I'm trying to code a C# UDP server. It receives a specific ID from the client, and return the song associated with it. The client is a PHP webpage, and stocks the bytes received into a file. Right now I'm doing some tests, trying to simply start a fake lecture of the song (just a javascript alert) when the transfer is at 2048 bytes. But I have plenty of bugs... The PHP page seems to finish the transfer into the file BEFORE having received all the data... The server continue to send bytes but the file is complete, with the good weight and all...

I know I don't have a very good english, so if you don't undersood something, just ask !

Here is the C# code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;  
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Data.SQLite;

namespace cdCollector
{
public partial class Streaming : Form
{
    private static List<IPAddress> clients_ = new List<IPAddress>();

    public Streaming()
    {
        InitializeComponent();
        listen();
    }

    public class ThreadClient
    {
        private static UdpClient socket_;
        private static IPEndPoint ipepClient_;
        private static int noChanson_;
        private static SQLiteConnection connexion_;

        public void setSocket(ref UdpClient socket) { socket_ = socket; }
        public void setIpepClient(ref IPEndPoint ipepClient) { ipepClient_ = ipepClient; }
        public void setNoChanson(int noChanson) { noChanson_ = noChanson; }
        public void setConnexion(ref SQLiteConnection connexion) { connexion_ = connexion; }

        public static void send()
        {
            try
            {
                while (Thread.CurrentThread.IsAlive)
                {
                    Chanson uneChanson;
                    FileStream stream;
                    byte[] buffer = new byte[1024];
                    int read;

                    uneChanson = new Chanson(noChanson_);
                    uneChanson.load(ref connexion_);

                    stream = new FileStream("C:\\Users\\Julie\\Documents\\toune.flac", FileMode.Open, FileAccess.Read);

                    socket_.Send(Encoding.ASCII.GetBytes(stream.Length.ToString()), stream.Length.ToString().Length, ipepClient_);

                    while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                        socket_.Send(buffer, buffer.Length, ipepClient_);

                    Console.WriteLine("finished");
                }
            }
            catch (ThreadAbortException tae)
            { }
            catch (Exception)
            {
                Thread.CurrentThread.Abort();
            }
        }
    }

    public static void listen()
    {
        byte[] data = new byte[1024];

        IPEndPoint ipepServer = new IPEndPoint(IPAddress.Any, 7575); // IP du serveur
        IPEndPoint ipepClient = new IPEndPoint(IPAddress.Any, 0); // IP du client;
        UdpClient socket = new UdpClient(ipepServer); // socket serveur
        int noChanson;
        SQLiteConnection connexion = new SQLiteConnection("Data Source=" + Application.StartupPath + "\\cdCollector.db");
        SQLiteCommand command = new SQLiteCommand(connexion);
        SQLiteDataReader dr;
        Thread thread;

        connexion.Open();

        while (true)
        {
            try
            {
                Console.WriteLine("Waiting for a client...");

                data = socket.Receive(ref ipepClient);

                Console.WriteLine("Message received from {0}:", ipepClient.ToString());
                Console.WriteLine(Encoding.ASCII.GetString(data, 0, data.Length));



                command.CommandText = "SELECT KeyLocale FROM AssocKeys WHERE NomTable = 'Chanson' AND KeyWeb = "
                                        + int.Parse(Encoding.ASCII.GetString(data, 0, data.Length));

                dr = command.ExecuteReader();


                if (dr.HasRows)
                {
                    dr.Read();

                    noChanson = dr.GetInt32(0);

                    dr.Close();

                    ThreadClient client = new ThreadClient();
                    client.setConnexion(ref connexion);
                    client.setIpepClient(ref ipepClient);
                    client.setNoChanson(noChanson);
                    client.setSocket(ref socket);

                    thread = new Thread(new ThreadStart(ThreadClient.send));
                    thread.Start();
                }
                else
                    socket.Send(Encoding.ASCII.GetBytes("Erreur: Chanson introuvable"), ("Erreur: Chanson introuvable").Length, ipepClient);



            }
            catch (SocketException se)
            {
                Console.WriteLine("Erreur Socket:" + se.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Erreur: " + ex.Message);
            }

        }

        connexion.Close();
    }

}   

}

And the PHP code:

<?php
 session_start();
$address="192.168.2.2";
$read = false;
$port = 7575;
$length = 0;
$started = false;

if (isset($port) and
($socket=socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) and
(socket_connect($socket, $address, $port)))
{
    $text =  "Connection successful on IP $address, port $port <br />";

    $from = '';
    $port = 0;
    $length = 0;

    socket_send( $socket, $_GET['no'], 1024, MSG_EOR );
    socket_recvfrom( $socket, $buf, 1024, 12, $from, $port);

    $lengthTotal = $buf;
    echo "Taille prévue du fichier: " . $lengthTotal . "<br />";

    if( file_exists( "temp" . $_SESSION['ID_Membre'] . ".flac" ) )
        unlink("temp" . $_SESSION['ID_Membre'] . ".flac");

    $file = fopen("temp" . $_SESSION['ID_Membre'] . ".flac", 'a');
    $buf = null;

    while( $length < $lengthTotal )
    {
        $length += socket_recvfrom( $socket, $buf, 1024, 12, $from, $port );
        if( $length > 2048 && !$started )
        {
            ?>
            <script type="text/javascript">
            <!--
                alert("Lecture...");
            //->
            </script>
            <?php
            $started = true;
        }

        fputs($file, $buf);

        flush();
    }

    echo "<br />" . $length . "<br />";
    fclose($file);
}
else
        $text="Unable to connect<pre>".socket_strerror(socket_last_error())."</pre>";

echo $text;
?>

Thanks a lot !

Upvotes: 1

Views: 6478

Answers (3)

Luis G. Costantini R.
Luis G. Costantini R.

Reputation: 1128

Some points:

1.- socket_recvfrom could return FALSE if there is any error, you can check the error with false === socket_recvfrom. 2.- If you are using a windows server add b to the open mode: $file = fopen("temp" . $_SESSION['ID_Membre'] . ".flac", 'ab'); (you are writing a binary file). 3.- Use as third argument of the fputs function the value returned by the socket_recvfrom function (if this value !== FALSE). 4.- You are using the value 12 (MSG_DONTROUTE | MSG_EOR), try to use 0 or MSG_WAITALL (of course socket_recvfrom is going to wait to receive 1024 bytes).

Your reception loop must be:

$reclen = 0;
while( ($reclen !== FALSE) && ($length < $lengthTotal) )
{
    $reclen = socket_recvfrom( $socket, $buf, 1024, 12, $from, $port );
    if ($reclen === FALSE)
    {
        echo "ERREUR";
        break;
    }
    $length += $reclen;
    if( $length > 2048 && !$started )
    {
        ?>
        <script type="text/javascript">
        <!--
            alert("Lecture...");
        //->
        </script>
        <?php
        $started = true;
    }

    fputs($file, $buf, $length);

    flush();
}

The problem is that you are adding the value returned by socket_recvfrom to $length if the return value is FALSE is going to add 0 to $length, that is the reason why you have to add an additional variable ($reclength).

Upvotes: 0

Richard Cook
Richard Cook

Reputation: 33109

UDP is an inherently unreliable transport. You will need to implement acknowledgements, timeouts, retransmissions and sequence numbers on top of UDP in order to guarantee transmission of all of your data in the expected order, unless your client application can live with dropped packets. I would advise you to consider using TCP sockets instead if you need guaranteed transmission of data between server and client and don't want to have to implement all of this stuff yourself (which might need to include client-side buffering to rearrange out-of-order datagrams). If you want reliability on top of UDP, I would advise you to read a good textbook on the subject (e.g. "Unix Network Programming" by W. Richard Stevens etc.).

Pointers on TCP:

You should take a look at System.Net.Sockets.TcpClient and System.Net.Sockets.TcpListener for the C# side of things and consult the PHP documentation for info on the PHP side of things.

Using TCP sockets isn't really that much different except you'll be using send and recv (or C#/PHP equivalents) instead of send_to and recv_from. Setting up the server side of things is a little more complicated since you need to bind and listen etc. but there are plenty of resources, e.g.:

http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server

Upvotes: 3

Julie
Julie

Reputation: 43

Thanks for your help. I changed what you told me, except adding 'b' to the fopen mode because my web server is on Ubuntu. I still receive plenty of errors to tell me that the client connection had to close... It seems like PHP think the download is finished and exit the loop, so it closes the connection of the socket. Also, many minutes after the page have load, the server is still sending data... I never did streaming before so I have difficulties to see where the problem is ...

Here's the new PHP code:

<?php
session_start();
$address="192.168.2.2";
$read = false;
$port = 7575;
$length = 0;
$started = false;

if (isset($port) and
($socket=socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) and
(socket_connect($socket, $address, $port)))
{
    $text =  "Connection successful on IP $address, port $port <br />";

    $from = '';
    $port = 0;
    $length = 0;

    socket_send( $socket, $_GET['no'], 1024, MSG_EOR );
    socket_recvfrom( $socket, $buf, 1024, MSG_WAITALL, $from, $port);

    $lengthTotal = $buf;
    echo "Taille prévue du fichier: " . $lengthTotal . "<br />";

    if( file_exists( "temp" . $_SESSION['ID_Membre'] . ".flac" ) )
        unlink("temp" . $_SESSION['ID_Membre'] . ".flac");

    $file = fopen("temp" . $_SESSION['ID_Membre'] . ".flac", 'a');
    $buf = null;

    while( $length !== FALSE && $length < $lengthTotal )
    {
        $length += socket_recvfrom( $socket, $buf, 1024, 12, $from, $port );
        if( $length > 2048 && !$started )
        {
            ?>
            <script type="text/javascript">
            <!--
                alert("Lecture...");
            //->
            </script>
            <?php
            $started = true;
        }

        if( $length == FALSE )
            echo "ERREUR";

        fputs($file, $buf, $length);

        flush();
    }

    echo "<br />" . $length . "<br />";
    fclose($file);
}
else
        $text="Unable to connect<pre>".socket_strerror(socket_last_error())."</pre>";

echo $text;
?>

Upvotes: 0

Related Questions