Как мне отправить несколько файлов по TCP одновременно в c Sharp?

У меня есть клиент-серверная реализация tcp, работающая в одной программе на разных фоновых рабочих потоках. Экземпляры этой программы будут на нескольких компьютерах, чтобы они могли отправлять и получать файлы между собой. Я могу отправлять файлы между компьютерами последовательно, используя сетевой поток, но как мне отправить несколько файлов одновременно с компьютера A на B.

Отправка нескольких файлов через одно соединение (сокет) - это нормально, но, имея несколько сетевых потоков, отправляющих данные клиенту, клиент не знает, какой фрагмент данных находится отдельно от какого файла?

Возможно ли, чтобы клиент дважды подключился к серверу (на другом порте, поскольку соединению назначается «случайный» / неиспользуемый порт), а затем каждое соединение имеет свой собственный поток, позволяющий отправлять 2 файла в то же время?

Спасибо за ваше время и усилия.


person Metalstorm    schedule 29.11.2010    source источник


Ответы (5)


Вы могли бы это сделать, но я не вижу в этом пользы. Если каждое соединение не регулируется где-то на линии, вы, по сути, несете вдвое больше накладных расходов по сравнению с операциями ввода-вывода.

Это то же самое, что и запись файла на диск, только то, что вы разделите его на два потока, не означает, что это будет быстрее, потому что диск может быть записан только за один раз. На самом деле вы можете увидеть более медленное время отклика.

person casperOne    schedule 29.11.2010
comment
Хотя, возможно, есть небольшие накладные расходы, подумайте об этом так; если бы я должен был отправить большой файл (скажем, 100 МБ +) кому-то на другом компьютере, если бы я затем захотел отправить небольшой текстовый файл, им пришлось бы ждать, пока большой файл будет в finsh, прежде чем они получат текстовый файл, где как если у меня несколько подключений, я могу отправить небольшой файл, пока отправляется большой файл. Я понимаю, о чем вы говорите, цель нескольких подключений / передач не в ускорении, а в том, чтобы разрешить параллельную отправку нескольких файлов. - person Metalstorm; 29.11.2010
comment
@Metalstorm: Если это приоритет, то да, я согласен, этот подход лучше, просто хотел убедиться, что я указал вам на это. - person casperOne; 29.11.2010

Клиент, безусловно, может подключаться к серверу несколько раз - и, вероятно, должен.

Однако вы можете указать тот же порт сервера - на стороне сервера будет назначен другой локальный порт, но клиенту не нужно об этом знать. (Подумайте о веб-сервере - множество клиентов будут подключаться к порту 80 одновременно.)

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

person Jon Skeet    schedule 29.11.2010
comment
Спасибо за подтверждение запроса на несколько подключений. - person Metalstorm; 29.11.2010

Вам необходимо использовать асинхронные клиентские и серверные сокеты. В основном вместо использования Recieve и Send используйте BeginRecieve, BeginSend, BeginConnect и BeginAccept. Таким образом, резьба будет выполнена за вас. Иметь каждое соединение в рабочем потоке - не лучшая идея. Таким образом, каждый новый запрос обрабатывается одновременно (асикронно). Вы также можете использовать первые несколько байтов отправленного файла для хранения данных о файле. Пример ниже. Конечно, числа (1,2,3 ..) ниже будут строковым именем файла, которое вы назвали Bitconverter.GetBytes (String var). надеюсь это поможет. [email protected] (скайп)

byte[] completefile = [1,2,3,4,5,6,7,8,9];
byte[] filename;
filename = split(lcomplefile, 3);  

Ниже приведен компилируемый пример Asyc Sockets.

using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
class AsyncTcpClient Form:
{
private TextBox newText;
private TextBox conStatus;
private ListBox results;
private Socket client;
private byte[] data = new byte[1024];
private int size = 1024;
public AsyncTcpClient()
{
Text = "Asynchronous TCP Client";
Size = new Size(400, 380);
Label label1 = new Label();
label1.Parent = this;
label1.Text = "Enter text string:";
label1.AutoSize = true;
label1.Location = new Point(10, 30);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
results = new ListBox();
results.Parent = this;
results.Location = new Point(10, 85);
results.Size = new Size(360, 18 * Font.Height);
Label label2 = new Label();
label2.Parent = this;
label2.Text = "Connection Status:";
label2.AutoSize = true;
label2.Location = new Point(10, 330);
conStatus = new TextBox();
conStatus.Parent = this;
conStatus.Text = "Disconnected";
conStatus.Size = new Size(200, 2 * Font.Height);
conStatus.Location = new Point(110, 325);
This document is created with the unregistered version of CHM2PDF Pilot
Button sendit = new Button();
sendit.Parent = this;
sendit.Text = "Send";
sendit.Location = new Point(220,52);
sendit.Size = new Size(5 * Font.Height, 2 * Font.Height);
sendit.Click += new EventHandler(ButtonSendOnClick);
Button connect = new Button();
connect.Parent = this;
connect.Text = "Connect";
connect.Location = new Point(295, 20);
connect.Size = new Size(6 * Font.Height, 2 * Font.Height);
connect.Click += new EventHandler(ButtonConnectOnClick);
Button discon = new Button();
discon.Parent = this;
discon.Text = "Disconnect";
discon.Location = new Point(295,52);
discon.Size = new Size(6 * Font.Height, 2 * Font.Height);
discon.Click += new EventHandler(ButtonDisconOnClick);
}
void ButtonConnectOnClick(object obj, EventArgs ea)
{
conStatus.Text = "Connecting...";
Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);
}
void ButtonSendOnClick(object obj, EventArgs ea)
{
byte[] message = Encoding.ASCII.GetBytes(newText.Text);
newText.Clear();
client.BeginSend(message, 0, message.Length, SocketFlags.None,
new AsyncCallback(SendData), client);
}
void ButtonDisconOnClick(object obj, EventArgs ea)
{
client.Close();
conStatus.Text = "Disconnected";
}
void Connected(IAsyncResult iar)
{
client = (Socket)iar.AsyncState;
try
{
client.EndConnect(iar);
conStatus.Text = "Connected to: " + client.RemoteEndPoint.ToString();
client.BeginReceive(data, 0, size, SocketFlags.None,
new AsyncCallback(ReceiveData), client);
} catch (SocketException)
{
conStatus.Text = "Error connecting";
}
}
void ReceiveData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int recv = remote.EndReceive(iar);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
results.Items.Add(stringData);
}
void SendData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
remote.BeginReceive(data, 0, size, SocketFlags.None,
This document is created with the unregistered version of CHM2PDF Pilot
new AsyncCallback(ReceiveData), remote);
}
public static void Main()
{
Application.Run(new AsyncTcpClient());
}
}
person user416134    schedule 05.02.2012
comment
Спасибо за ответ user416134, но этот вопрос довольно старый. С тех пор я решил проблему (на C #), закончил свое решение, а затем перешел на java и реализовал его с нуля. - person Metalstorm; 05.02.2012

Возможно ли, чтобы клиент дважды подключился к серверу (на другом порте, поскольку соединению назначается «случайный» / неиспользуемый порт), а затем каждое соединение имеет свой собственный поток, позволяющий отправлять 2 файла на то же время?

Да; так обычно работают сетевые протоколы. Вам не нужно выбирать новый номер порта на стороне сервера: даже если вы слушаете фиксированный номер порта, соединения с этим портом сохраняются отдельно.

Например, веб-сервер на www.stackoverflow.com всегда прослушивает порт 80, но мы с вами можем подключаться из наших веб-браузеров, и наши подключения не смешиваются.

person Tim Robinson    schedule 29.11.2010
comment
Спасибо, что подкрепили предыдущий ответ, я думаю, что это путь, который я выберу - person Metalstorm; 29.11.2010

Вам нужно либо наложить протокол на один сокет, чтобы определить, какие данные являются частью какого файла, либо использовать несколько сокетов, чтобы знать, где начинается и заканчивается каждый файл.

Даже с несколькими сокетами у вас должны быть маркеры начала / конца, если вы хотите повторно использовать один и тот же сокет для нового файла без обслуживания открытия / закрытия сокета.

Почему вы не можете использовать FTP?

person Steve Townsend    schedule 29.11.2010
comment
@Steve Townsend Я думаю, что для простоты я пойду по маршруту нескольких соединений, я могу видеть это яснее в моей голове, вызывая каждое соединение, являющееся туннелем / трубой, и данные, отправляемые по нему :), а не один канал с большим количеством запусков и конечные биты :) Я понимаю tcp настолько, что могу это сделать (я думаю), я использовал FTP только для локального ПК - ›Интернет -› удаленный сервер. Можете ли вы ftp по локальной сети? (я предполагаю, что вы можете), и я не понимаю (поскольку я не смотрел) основную механику ftp - person Metalstorm; 29.11.2010
comment
Если вы делаете одноранговые xfrs, это в любом случае имеет больше смысла, чем FTP. Вы всегда можете просто закрыть сокет, и тогда получатель узнает, что у него есть полный файл. Возможно, стоит отправить длину файла заранее, в зависимости от того, насколько надежным он вам нужен, и / или использовать безопасный канал: msdn.microsoft.com/en-us/library/ - person Steve Townsend; 29.11.2010
comment
В настоящее время я использую этот метод перед отправкой файла, я отправляю сообщение, содержащее следующее имя файла размера КОМАНДЫ. Например, BEGINSEND 1024 testfile.zip, чтобы сервер знал, что файл поступает, и запускал функцию для «захвата» данных для файла. По сути, while (sentData ‹size) - person Metalstorm; 29.11.2010
comment
Похоже, вы уже отправили некоторую контрольную информацию, чтобы провести небольшую проверку ошибок. Получатель может использовать этот факт для резервирования места на диске для оптимизации файлов, которые создаются путем чередующейся записи. - person Steve Townsend; 29.11.2010
comment
Я собираюсь начать работу над этим сейчас, еще один вопрос: каждому соединению (сокету) нужен собственный фоновый рабочий, чтобы иметь возможность отправлять данные одновременно. Или, в основном, для foreach (файл в файлах) {socket.BeginSend (...); } Работа? я думаю, что BeginSend сам по себе действует как нить? - person Metalstorm; 29.11.2010
comment
Для обеспечения параллелизма сетевой отправки вам действительно нужно несколько потоков. Асинхронный ввод-вывод (как вы отметили) будет работать - если вы используете .Net 4., вы можете использовать параллельную библиотеку задач, чтобы упростить рассмотрение / проектирование. - person Steve Townsend; 29.11.2010
comment
Еще раз спасибо за помощь, я собираюсь взломать это :) - person Metalstorm; 29.11.2010