Могут ли данные быть отправлены через сокет, который вызвал BeginReceive, но еще не EndReceive? (Включает полный код для файлового туннеля :) - PullRequest
1 голос
/ 19 мая 2011

Я открыл сокет между двумя машинами.На обоих концах они вызывают BeginReceive и ждут друг друга, чтобы отправить данные.Либо можно отправить данные в первую очередь, это зависит от пользователя.

Я обнаружил, что не могу отправить данные в обе стороны, и только приложение, инициировавшее соединение, может отправлять данные.

Редактировать: Добавлен код для программы File Tunnel;добавлен комментарий, показывающий, где я испортил

//-----------Main.cs file------------
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.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;

public Socket listen_socket = null; //Listening socket (activated by clicking a button)
public List<Socket> connections = new List<Socket>(); //Stores all active connections to other server/client hybrids
public List<FileReceiver> receivers = new List<FileReceiver>(); //One FileReceiver for each active connection, handles receiving files on the connection
public List<FileSender> senders = new List<FileSender>(); //One FileSender per connection when a file is dropped in the interface, handles sending the file to each connected instance
public Queue<string> files_to_send = new Queue<string>(); //Queues files to send, if multiple files are dropped in the interface at once

public Main()
{
    InitializeComponent();
    Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
    lstFiles.DragEnter += new DragEventHandler(lstFiles_DragEnter);
    lstFiles.DragDrop += new DragEventHandler(lstFiles_DragDrop);
}

private void lstFiles_DragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.All;
}

void lstFiles_DragDrop(object sender, DragEventArgs e)
{
    string[] filepaths = e.Data.GetData( DataFormats.FileDrop ) as string[];
    if (filepaths != null)
    {
        lock (files_to_send)
        {
            foreach (string filepath in filepaths)
                files_to_send.Enqueue( filepath );
            SendNextFile();
        }
    }
}

void SendNextFile()
{
    lock (files_to_send)
    {
        if (senders.Count == 0 && files_to_send.Count > 0)
        {
            string filepath = files_to_send.Dequeue();
            FileInfo fi = new FileInfo( filepath );
            string filename = fi.Name;

            MemoryStream ms = new MemoryStream();
            BinaryWriter writer = new BinaryWriter( ms, Encoding.Unicode );
            writer.Write( (int)filename.Length ); //known-length (4 bytes); serves as hint to protocol so it doesn't try to read string until it knows enough data has been received
            writer.Write( filename ); //known length once previous int has been read
            writer.Write( fi.CreationTimeUtc.ToBinary() ); //known-length (8 bytes)
            writer.Write( fi.LastWriteTimeUtc.ToBinary() ); //known-length (8 bytes)
            writer.Write( fi.LastAccessTimeUtc.ToBinary() ); //known-length (8 bytes)
            writer.Write( (long)fi.Length ); //known-length (8 bytes)
            byte[] header = ms.ToArray();

            foreach (Socket socket in connections)
            {
                FileSender filesender = new FileSender( fi, header, socket );
                filesender.SendComplete += new FileSenderCompletedEventHandler(filesender_SendComplete);
                senders.Add( filesender );
            }
        }
    }
}

void filesender_SendComplete(FileSender sender)
{
    lock (files_to_send)
    {
        senders.Remove( sender );
        if (senders.Count == 0)
            SendNextFile();
    }
    if (!sender.CompletedSuccessfully)
        MessageBox.Show( "Failed to send " + sender.fi.FullName + " to " + sender.socket.RemoteEndPoint.ToString() );
}

void Application_ApplicationExit(object sender, EventArgs e)
{
    lock (connections)
    {
        foreach (Socket socket in connections)
        {
            try
            {
                socket.Shutdown( SocketShutdown.Both );
                socket.Close();
            }
            catch (Exception err)
            {
            }
        }
        connections.Clear();
    }
}

private void btnListen_Click(object sender, EventArgs e)
{
    try
    {
        btnListen.Enabled = false;
        if (listen_socket != null)
            listen_socket.Close();
        listen_socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
        listen_socket.Bind( new IPEndPoint( IPAddress.Any, int.Parse( txtListenPort.Text.Trim() ) ) );
        listen_socket.Listen( 1 );
        listen_socket.BeginAccept( new AsyncCallback( handleAccept ), listen_socket );
    }
    catch (Exception err)
    {
        MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
        btnListen.Enabled = true;
        if (listen_socket != null)
            listen_socket.Close();
    }
}

//INITIALIZES CONNECTION ON LISTENING END
private void BeginAccept_Callback( IAsyncResult result )
{
    Socket listen_socket = result.AsyncState as Socket;
    try
    {
        Socket connection = listen_socket.EndAccept( result );
        lock (connections)
        {
            ConfigureSocket( connection );
            FileReceiver receiver = AddSocketConnection( connection );
            receiver.receiveData(); //<--WAS MISSING THIS!!! Initiates asynchronous BeginReceive call; FileReceiver handles processing the incoming stream as it arrives.
        }
    }
    catch (Exception err)
    {
        MessageBox.Show( err.Message );
    }
    listen_socket.Close();
}

private void btnConnect_Click(object sender, EventArgs e)
{
    string[] a = txtConnectIP.Text.Split( '.' );
    IPAddress address = new IPAddress( new byte[] {byte.Parse( a[0] ), byte.Parse( a[1] ), byte.Parse( a[2] ), byte.Parse( a[3] ) } );
    int port = int.Parse( txtConnectPort.Text );
    Connect( address, port );
}

//INITIALIZES CONNECTION ON CONNECTING END
private void Connect( IPAddress address, int port )
{
    Socket connection = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
    lock (connections)
    {
        try
        {
            ConfigureSocket( connection );
            connection.Connect( address, port );
            FileReceiver receiver = AddSocketConnection( connection );
            receiver.receiveData(); //<--REMEMBERED IT HERE!!! So I could receive files on the connecting end.
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message );
        }
    }
}

private void DisplayConnectionsCount( int count )
{
    txtConnections.Text = count.ToString();
}

private void ConfigureSocket( Socket connection )
{
    connection.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true );
    connection.UseOnlyOverlappedIO = true;
    connection.LingerState = new LingerOption( false, 0 );
}

private FileReceiver AddSocketConnection( Socket connection )
{
    lock (connections)
    {
        connections.Add( connection );
        Invoke( new Action<string>( DisplayConnectionCount ), connections.Count );
        FileReceiver receiver = new FileReceiver( connection );
        receivers.Add( receiver );
        receiver.ReceiverShutdown += new FileReceiverShutdownEventHandler( receiver_Shutdown );
        receiver.FileReceived += new FileReceiverReceiveEventHandler(receiver_FileReceived);
        receiver.ReceiverDownloading += new FileReceiverDownloadingEventHandler(receiver_ReceiverDownloading);
        return receiver;
    }
}

private void RemoveSocketConnection( FileReceiver receiver )
{
    lock (connections)
    {
        try
        {
            receiver.ReceiverShutdown -= new FileReceiverShutdownEventHandler( receiver_Shutdown );
            receiver.FileReceived -= new FileReceiverReceiveEventHandler(receiver_FileReceived);
            receiver.ReceiverDownloading -= new FileReceiverDownloadingEventHandler(receiver_ReceiverDownloading);
            receivers.Remove( receiver );
            connections.Remove( receiver.connection );
            Invoke( new Action<int>( DisplayConnectionsCount ), connections.Count );
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
        }
    }
}

void receiver_ReceiverDownloading(FileReceiver receiver)
{
    if (InvokeRequired)
        Invoke( new FileReceiverDownloadingEventHandler( receiver_ReceiverDownloading ), receiver );
    else
        Text = "File Tunnel - Received " + receiver.BytesReceived + " bytes...";
}

void receiver_FileReceived(FileInfo fi)
{
    if (lstFiles.InvokeRequired)
        lstFiles.BeginInvoke( new FileReceiverReceiveEventHandler( receiver_FileReceived ), fi );
    else
    {
        lstFiles.Items.Add( fi );
        Text = "File Tunnel";
    }
}

public void receiver_Shutdown( FileReceiver receiver )
{
    RemoveSocketConnection( receiver );
}

//----FileSender.cs file------
public delegate void FileSenderCompletedEventHandler( FileSender sender );
public class FileSender
{
    public Socket socket;
    public FileInfo fi;
    public FileStream fs;

    public event FileSenderCompletedEventHandler SendComplete;
    private byte[] header;
    public bool CompletedSuccessfully = false;

    public FileSender( FileInfo fi, byte[] header, Socket socket )
    {
        this.fi = fi;
        this.socket = socket;
        this.header = header;
        try
        {
            socket.BeginSendFile( fi.FullName, header, null, TransmitFileOptions.UseSystemThread, new AsyncCallback( send_File ), this );
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
            CompleteCallback();
        }
    }

    private void send_File( IAsyncResult result )
    {
        try
        {
            socket.EndSendFile( result );
            CompletedSuccessfully = true;
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
        }
        finally
        {
            CompleteCallback();
        }
    }

    private void CompleteCallback()
    {
        try
        {
            if (SendComplete != null)
                SendComplete( this );
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
        }
    }
}


//----FileReceiver.cs file------
public delegate void FileReceiverReceiveEventHandler( FileInfo fi );
public delegate void FileReceiverShutdownEventHandler( FileReceiver receiver );
public delegate void FileReceiverDownloadingEventHandler( FileReceiver receiver );

public enum FileReceiverProtocolStep
{
    ReadFilenameLength,
    ReadFilename,
    ReadTimestamps,
    ReadFileLength,
    ReadFile
}

public class FileReceiver
{
    public const int BUFFER_SIZE = 1024 * 1024;
    public Socket connection;
    private byte[] buffer = new byte[BUFFER_SIZE];

    private long last_read_position = 0;
    private FileReceiverProtocolStep protocol_step = FileReceiverProtocolStep.ReadFilenameLength;

    private MemoryStream received_data = new MemoryStream();

    public event FileReceiverReceiveEventHandler FileReceived;
    public event FileReceiverShutdownEventHandler ReceiverShutdown;
    public event FileReceiverDownloadingEventHandler ReceiverDownloading;

    public FileReceiver( Socket connection )
    {
        this.connection = connection;
    }

    public long BytesReceived
    {
        get {return received_data.Length;}
    }

    public void receiveData()
    {
        connection.BeginReceive( buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback( receiveDataCallback ), connection );
    }

    private void receiveDataCallback( IAsyncResult result )
    {
        Socket connection = result.AsyncState as Socket;
        int bytes_read;

        try
        {
            bytes_read = connection.EndReceive( result );
            if (bytes_read == 0)
            {
                if (received_data.Length > 0)
                    ProcessMemoryStream();
                else
                {
                    ShutDown(); //Nothing recieved... socket probably closed
                    return;
                }
            }
            else
            {
                lock (received_data)
                    received_data.Write( buffer, 0, bytes_read );
                ProcessMemoryStream(); //Process as much of the memory stream as possible
                if (ReceiverDownloading != null)
                    ReceiverDownloading( this );
            }
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
            ShutDown();
            return;
        }

        receiveData();
    }

    private void ShutDown()
    {
        try
        {
            connection.Shutdown( SocketShutdown.Both );
            connection.Close();
        }
        catch (Exception err)
        {
            MessageBox.Show( err.Message + "\r\n" + err.StackTrace );
        }

        if (ReceiverShutdown != null)
            ReceiverShutdown( this );
    }

    private int filename_length;
    private string filename;
    private DateTime timestamp_creation;
    private DateTime timestamp_modified;
    private DateTime timestamp_lastaccess;
    long file_length;

    private void ProcessMemoryStream()
    {
        //Prepare binary reader
        lock (received_data)
        {
            BinaryReader reader = new BinaryReader( received_data, Encoding.Unicode );
            received_data.Position = last_read_position;

        next_step:
            long bytes_available = received_data.Length - received_data.Position;
            switch (protocol_step)
            {
                case FileReceiverProtocolStep.ReadFilenameLength:
                    //Read filename
                    if (bytes_available >= 4)
                    {
                        filename_length = reader.ReadInt32();
                        protocol_step = FileReceiverProtocolStep.ReadFilename;
                        goto next_step;
                    }
                    break;
                case FileReceiverProtocolStep.ReadFilename:
                    if (bytes_available >= filename_length)
                    {
                        filename = reader.ReadString();
                        protocol_step = FileReceiverProtocolStep.ReadTimestamps;
                        goto next_step;
                    }
                    break;
                case FileReceiverProtocolStep.ReadTimestamps:
                    if (bytes_available >= 24)
                    {
                        //Read timestamps
                        timestamp_creation = DateTime.FromBinary( reader.ReadInt64() );
                        timestamp_modified = DateTime.FromBinary( reader.ReadInt64() );
                        timestamp_lastaccess = DateTime.FromBinary( reader.ReadInt64() );
                        protocol_step = FileReceiverProtocolStep.ReadFileLength;
                        goto next_step;
                    }
                    break;
                case FileReceiverProtocolStep.ReadFileLength:
                    if (bytes_available >= 8)
                    {
                        file_length = reader.ReadInt64();
                        protocol_step = FileReceiverProtocolStep.ReadFile;
                        goto next_step;
                    }
                    break;
                case FileReceiverProtocolStep.ReadFile:
                    if (bytes_available >= file_length)
                    {
                        FileInfo fi = new FileInfo( filename.Replace( '\\', '_' ).Replace( '/', '_' ) ); //Disable relative path specification
                        FileStream fs = fi.Open( FileMode.Create, FileAccess.Write, FileShare.Read );
                        long bytes_to_save = file_length;
                        while (bytes_to_save > 0)
                        {
                            int bytes_to_read = (int)Math.Min( (long)BUFFER_SIZE, bytes_to_save );
                            bytes_to_save -= bytes_to_read;
                            fs.Write( reader.ReadBytes( bytes_to_read ), 0, bytes_to_read );
                        }
                        fs.Close();
                        fi.CreationTimeUtc = timestamp_creation;
                        fi.LastWriteTimeUtc = timestamp_modified;
                        fi.LastAccessTimeUtc = timestamp_lastaccess;

                        received_data.Position = 0;
                        received_data.SetLength( 0 );

                        //Reset protocol for next file
                        protocol_step = FileReceiverProtocolStep.ReadFilenameLength; //Ready for next file
                        filename_length = 0;
                        filename = String.Empty;
                        timestamp_creation = DateTime.MinValue;
                        timestamp_modified = DateTime.MinValue;
                        timestamp_lastaccess = DateTime.MinValue;
                        file_length = 0;

                        if (FileReceived != null)
                            FileReceived( fi );
                    }
                    break;
            }

            //Backup the last read position, and set the position to the end of the stream for subsquent write operations
            last_read_position = received_data.Position;
            received_data.Seek( 0, SeekOrigin.End );
        }
    }
}

Так что это код на данный момент.Извините за неряшливость в этом коде, я написал его как можно быстрее, но это целая программа на данный момент, и она полностью функциональна (Main.cs, FileReceiver.cs и FileSender.cs).Форма состоит из списка, нескольких кнопок и текстовых полей.На данный момент обратная связь является чрезвычайно простой (отображает байты, полученные в заголовке заголовка, по мере получения файла).В конце концов, мне нужно будет проанализировать IP-адреса с RegExes с обратной связью в реальном времени, принять имена хостов с DNS-распознавателями, я заменю список на просмотр списка в стиле проводника, сохраню его в специальном каталоге, разрешу файлыперетаскивать / перемещать в проводник и т. д. и т. п.

1 Ответ

1 голос
/ 19 мая 2011

Я не вызывал BeginReceive в коде, который принимает соединение на конце прослушивания (см. Комментарий в методе BeginAccept_Callback в опубликованном коде.)

Теперь я могу с уверенностью сказать, что можно вызывать BeginReceive для сокета и вызывать также методы BeginSend * для него в любое время, чтобы можно было одновременно получать и отправлять данные через один сокет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...