我在两台机器之间打开了一个插座。在两端,他们调用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解析器的主机名,我将使用资源管理器样式列表视图替换列表框,将其存储在特殊目录中,允许文件存在拖动/移动到资源管理器等等。
答案 0 :(得分:1)
我没有在接受侦听端连接的代码中调用BeginReceive(请参阅发布代码中的BeginAccept_Callback方法中的注释。)
我现在可以肯定地说,可以在套接字上调用BeginReceive并随时调用它上面的BeginSend *方法,这样就可以通过一个Socket同时接收和发送数据。