【问题标题】:UDP Streaming transfer from C# application to PHP webpage从 C# 应用程序到 PHP 网页的 UDP 流传输
【发布时间】:2013-12-18 18:27:37
【问题描述】:

我正在尝试编写 C# UDP 服务器。它从客户端接收特定的 ID,并返回与其关联的歌曲。客户端是一个 PHP 网页,并将接收到的字节存储到一个文件中。现在我正在做一些测试,试图在传输为 2048 字节时简单地开始一个虚假的歌曲讲座(只是一个 javascript 警报)。但是我有很多错误...... PHP页面似乎在收到所有数据之前完成了文件的传输......服务器继续发送字节但文件是完整的,重量很好......

我知道我的英语不是很好,所以如果你不理解某些事情,尽管问!

这是 C# 代码:

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();
    }

}   

}

还有 PHP 代码:

<?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;
?>

非常感谢!

【问题讨论】:

  • 2 事情,Web 客户端从文件/数据库中读取,并让另一个程序使用新歌曲信息更新文件/数据库不是更好吗? UDP 被认为是一个不可靠的传输系统,所以理论上你可能永远不会得到任何回报。在这种情况下,您将永远等待
  • 是的,我认为这是个好主意 :) 谢谢!

标签: php streaming


【解决方案1】:

UDP 本质上是一种不可靠的传输方式。您将需要在 UDP 之上实现确认、超时、重传和序列号,以保证以预期的顺序传输所有数据,除非您的客户端应用程序可以忍受丢弃的数据包。如果您需要在服务器和客户端之间保证数据传输并且不想自己实现所有这些东西(可能需要包括客户端缓冲以重新排列序数据报)。如果你想在 UDP 之上获得可靠性,我建议你阅读一本关于该主题的好教科书(例如 W. Richard Stevens 的“Unix Network Programming”等)。

TCP 上的指针:

您应该查看 System.Net.Sockets.TcpClientSystem.Net.Sockets.TcpListener 了解 C# 方面的内容,并咨询 PHP documentation 了解有关 PHP 方面的信息。

使用 TCP 套接字并没有太大的不同,只是您将使用 sendrecv(或 C#/PHP 等效项)而不是 send_torecv_from。设置服务器端有点复杂,因为您需要 bindlisten 等,但是有很多资源,例如:

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

【讨论】:

  • 哇,我没看到你的帖子...我听说 UDP 是最好的流媒体协议,也许我错了?当然,使用 TCP 对我来说会更容易......你能给我举个例子来说明我如何做到这一点吗?
  • @Julie:UDP 更适合需要避免 TCP 等面向连接协议的开销(启动连接的三次握手等)的应用程序,但您需要像我一样做额外的工作描述。在 TCP 编程链接上查看我的额外 cmets。
  • 是的,我了解 TCP,我已经使用该协议编写了​​一些应用程序 :) 感谢您的帮助,我开始工作了! :)
【解决方案2】:

感谢您的帮助。我更改了您告诉我的内容,除了将“b”添加到 fopen 模式,因为我的 Web 服务器在 Ubuntu 上。我仍然收到很多错误告诉我客户端连接必须关闭......似乎 PHP 认为下载已完成并退出循环,所以它关闭了套接字的连接。此外,页面加载后几分钟,服务器仍在发送数据......我以前从未做过流媒体,所以我很难看出问题出在哪里......

这是新的 PHP 代码:

<?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;
?>

【讨论】:

    【解决方案3】:

    几点:

    1.- socket_recvfrom 如果有任何错误可能会返回 FALSE,您可以使用 false === socket_recvfrom 检查错误。 2.- 如果您使用的是 Windows 服务器,请将 b 添加到打开模式: $file = fopen("temp" . $_SESSION['ID_Membre'] . ".flac", 'ab'); (您正在编写一个二进制文件)。 3.- 使用 socket_recvfrom 函数返回的值作为 fputs 函数的第三个参数(如果这个值!== FALSE)。 4.- 您使用的值是 12 (MSG_DONTROUTE | MSG_EOR),尝试使用 0 或 MSG_WAITALL(当然 socket_recvfrom 会等待接收 1024 个字节)。

    您的接收循环必须是:

    $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();
    }
    

    问题在于,如果返回值为 FALSE,则将 socket_recvfrom 返回的值添加到 $length 中,这将在 $length 中添加 0,这就是您必须添加附加变量 ($reclength) 的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-30
      • 1970-01-01
      • 2015-11-22
      • 2015-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多