可以通过调用BeginReceive而不是EndReceive的Socket发送数据吗? (包括文件隧道的完整代码:)

时间:2011-05-18 20:54:31

标签: .net sockets asynchronous

我在两台机器之间打开了一个插座。在两端,他们调用BeginReceive并等待彼此发送数据。任何一个都可以先发送数据,这取决于用户。

我发现我无法双向发送数据,只有发起连接的应用才能发送数据。

编辑:添加了文件隧道程序的代码;添加评论显示我搞砸了哪里

//-----------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)。表单由listBox和几个按钮和文本框组成。此时反馈非常基本(显示标题栏中接收文件时收到的字节)。最后,我将使用RegExes解析IP地址并提供实时反馈,接受带有DNS解析器的主机名,我将使用资源管理器样式列表视图替换列表框,将其存储在特殊目录中,允许文件存在拖动/移动到资源管理器等等。

1 个答案:

答案 0 :(得分:1)

我没有在接受侦听端连接的代码中调用BeginReceive(请参阅发布代码中的BeginAccept_Callback方法中的注释。)

我现在可以肯定地说,可以在套接字上调用BeginReceive并随时调用它上面的BeginSend *方法,这样就可以通过一个Socket同时接收和发送数据。