Передача файлов c

Передача файлов через TCP на C++

Передача файлов c

Как-то меня попросили написать программу, которая из определенной папки передает mp3 файлы с одного ПК на другой. Программка сканировала папку каждые 5 минут и найденные там файлы трелевала на соседний компьютер.

Сканирование было реализовать нетрудно, а вот с сетью я никогда до этого не работал. Пришлось разбираться с этим с нуля. Покурив кое-какие мануалы в интернете, я полностью разобрался с передачей файлов по сети.

Чем и хочу поделиться с тобой в этой статье.

Для начала разберемся как передается информация через TCP/IP

Логично предположить, что для передачи информации нужно как минимум надо 2 компьютера: один – клиент(получатель), второй – сервер(отправитель). Клиент должен соединиться с сервером, а сервер, в свою очередь, должен разрешить соединение.

Исходя из этого, делаем несложный вывод: сервер должен быть запущен до того, как клиент попробует соединиться с ним. IP-адрес серверу устанавливать не надо, т.к. он уже установлен (ведь сервер – компьютер подключенный к сети, а значит имеет свой адрес), а вот порт указать придется, портом может быть любое число от 1 до 65535.

Чаще всего порты от 1 до 1024 заняты системой, поэтому предпочтительней указывать от 1024 и дальше.

Клиенту не так повезло как серверу, т.к. клиенту надо «открыть» сокет. Т.е. указать IP-адрес сервера и порт по которому будет происходить соединение. Причем порт, указанный клиентом, должен совпадать с портом, указанным на сервере.

Когда клиент «открыл» сокет, он соединяется с сервером. При соединении клиент «сообщает» серверу о своем существовании, и сервер, «услышав» клиента, открывает новый сокет для этого клиента.

В конце концов, создается сокет соединение (TCP-подключение между клиентом и сервером).

По этому соединению и осуществляется процесс передачи информации. Информация разбивается на пакеты и передается от сервера к клиенту(хотя TCP разрешает передачу байтов в обоих направлениях, в нашей программе передача будет происходить от сервера к клиенту). TCP обеспечивает полноценную передачу данных без нарушений и искажений в таком же порядке, в котором отправитель послал их адресату.

Важно отметить, что TCP следит, чтобы информация не была повреждена во время передачи. Адресацию и маршрутизацию пакетов выполняет IP (Internet Protocol).
Таким образом, TCP взаимодействует с протоколом клиентского приложения и с IP.

На самом деле, все намного сложнее, чем я описал, но этих знаний должно быть достаточно, чтобы понять принцип работы приложения. Подробно с работой TCP протокола можно ознакомиться тут: http://devoid.com.ua/c-builder/cppbuilder-network-programming/podrobnoe-…

Проектирование программы

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

Сервер

Основным компонентом сервера будет TServerSocket (находится во вкладке FastNet панели компонентов). Сейчас я кратко опишу свойства и метода этого компонента, которые нам понадобятся в ходе написания программы.

Active – показывает, открыт сокет или нет. Тип: bool. Соответственно, true – открыт, а false – закрыт.Port – порт, по которому будет проходить соеденение.

ServerSocket ->Open() – открытие сокета (аналогично присвоению значения True свойству Active);ServerSocket->Socket->Connections[i]->SendText(String str) – отправляет i-тому подключенному пользователю текстовую строку через сокет.ServerSocket->Socket->Connections[i]->SendBuf(void buf, int count) – отправляет i-тому подключенному пользователю буфер через сокет.

Буфером может быть любой тип данных. Буфер указывается параметром buf, параметр count – размер пересылаемых данных в байтах.

Server->Socket->Connections[i]->ReceiveText() – полученный через сокет текст от i-того подключения.

Так же нам понадобиться компонент TOpenDialog (вкладка Dialogs), чтобы указать путь к файлу, который мы хотим передать. И, наконец, компонент TButton (вкладка Standart), который будет запускать процедуру передачи файла.

Клиент

Основным компонентом клиента будет TClientSocket (находится в вкладке FastNet панели компонентов). Так же как и в ситуации с сервером я опишу, нужные нам, свойства и методы.

Active – показывает открыт сокет или нет. Тип: bool. Соответственно, true – открыт, а false – закрыт.Address – адрес сервера.ClientSocket->Socket->SendText(String str) – посылка текстовой строки через сокет.

ClientSocket ->Socket->ReceiveText() – полученный через сокет текст.

ClientSocket ->Open() – открытие сокета (аналогично присвоению значения True свойству Active);
OnRead – возникает, когда удаленный компьютер послал тебе какие-либо данные.

Так же нам понадобится компонент TButton.

Кроме TClientSocket и TServerSocket еще одним важным моментом является класс, обеспечивающий сохранение данных в адресном пространстве – TmemoryStream. Класс позволяет использовать адресное пространство для хранения промежуточных результатов работы приложения, а также при помощи стандартных методов осуществлять обмен данными между памятью и другими физическими носителями.

Методы и свойства класса, которые понадобятся нам при написании программы:LoadFromFile(String filename) – запись данных в память из файла.Position – возвращает или задает текущее положение в потоке.

Memory – определяет область памяти, выделенную для хранения потока.Clear() – очищает поток.Size – размер данных помещенных в поток.Write(char buf, int size) – записывает в текущий поток блок байтов(размером в size), используя данные, считанные из буфера buf.

SaveToFile(String filename) – сохраняет данные из потока в файл.

Перейдем к коду

Для начала определим «шаблон» нашего потока, она выглядит следующим образом: file#filename#filesize#.

Когда клиент принимает такую строку, он обрабатывает ее(выделяет имя файла и его размер), создает буфер размером filesize и в него пишет всю последующую информацию.

Когда размер переданной информации равен размеру файла, посылает на сервер команду “Ready!”, сервер обрабатывает эту команду и закрывает поток.

Теперь мы владеем информацией, достаточной для написания программы. Поэтому начнем.

Сервер

В инспекторе объекта TServerSocket измени следующие свойства:Name: ServerПорт укажите свой. Это можно сделать в инспекторе объектов. Свойство Active поставь в значение true(сервер изначально будет запущен).

Теперь объявим глобальную переменную – поток, который будет передан клиенту.

TMemoryStream *Stream=new TMemoryStream;

В обработчик события OnClick кнопки пишем следующий код:

void *filePointer; //область памяти для хранения данных потока.int fileSize; //размер файла, который будем передавать. if(OpenDialog->Execute()) Stream->LoadFromFile(OpenDialog->FileName); //загружаем в поток выбранный файл.

 Server->Socket->Connections[0]->SendText(“file#” + OpenDialog->FileName + “#” + IntToStr(Stream->Size) + “#”); //формируем информацию о файле, в соответствии с //«шаблоном», описанным выше. Stream->Position=0; // устанавливаем поток в начальную позицию. filePointer=Stream->Memory; //записываем в указатель область памяти в которой храниться //поток.

fileSize =Server->Socket->Connections[0]->SendBuf(filePointer, Stream->Size); //отправляем //содержимое области памяти клиенту.

В событие ClientRead пишем:

if(Server->Socket->Connections[0]->ReceiveText()==”Ready!”) //если получили от клиента //команду, что файл принят. Stream->Clear(); //очищаем поток.

Сервер готов. Тут приведен простенький пример, описывающий только процесс отсылки. При желании можно сюда добавить Memo для ведения истории, ProgressBar для информирования о ходе передачи и т.д. Это сделаешь сам по желанию.

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

Клиент

Форму назови RecieverForm.В инспекторе объектов TClietnSocket указываем следующие свойства:Name: ClientPort: порт, который ты указал на сервере.Host : адрес сервера.

В .h файле объяви прототип функции, которая будет сохранять полученную информацию в файл:

void SaveInfo(AnsiString info );

В .cpp файле объяви следующие глобальные переменные:

TMemoryStream *Stream = new TMemoryStream ;int fileSize ; //размер полученного файла.bool isReceive ; //происходит ли процесс передачи или нет.AnsiString fileName ; //имя файла.

Теперь в .cpp файле опишем саму функцию:

void SaveInfo(AnsiString info ){if(Stream->SizeWrite(info.c_str(), info.Length()); //записываем в поток информацию о файле if(Stream->Size==fileSize) //если размер потока равен размеру данных(файл принят) { isReceive=false; //указываем, что процесс получения закончен.  Stream->Position=0; // устанавливаем поток в начальную позицию.

RecieverForm->Client->Socket->SendText(“Ready!”); //сообщаем серверу, что файл у нас. CreateDir(“Downloads”); //создаем в папке(из которой запущена программа) папку Dwnlds. Stream->SaveToFile(“Downloads\\” + fileName); //сохраняем поток в эту папку. Stream->Clear(); //очищаем поток. fileSize=0; //обнуляем переменную с размером файла.

}

В обработчик события OnRead объекта Client введи следующий код:

AnsiString recieveText=Client->Socket->ReceiveText(); //запоминаем текст, переданный //сервером if(isReceive) //если файл еще передается SaveInfo (recieveText); //записываем его в поток.else { if(recieveText.SubString(0,recieveText.

Pos(“#”)-1)==”file”) // если это строка типа // file#filename#filesize#, то начинаем обрабатывать полученную информацию : { recieveText.Delete(1,recieveText.Pos(“#”)); //удаляем слово file name=recieveText.SubString(0,recieveText.Pos(“#”)-1); //определяем имя файла(путь+имя). fileName=name.

SubString(name.LastDelimiter(“\\”)+1,name.Length()); //выделяем чистое //имя файла. recieveText.Delete(1,recieveText.Pos(“#”)); //удаляем имя файла. fileSize=StrToInt(recieveText.SubString(0,recieveText.Pos(“#”)-1)); //определяем размер. recieveText.Delete(1,recieveText.

Pos(“#”)); //удаляем последний разделитель. isReceive=true; //указваем, что файл принимается. } }

При нажатии на кнопку пишем:

Вот и все. Сюда, так же как и на сервер, можно добавить Memo, ProgressBar, Timer и т.д.

Из-за того, что тут используется объект класса TMemoryStream, нельзя передать файлы большого размера(у меня на компьютере с 1280МБ ОЗУ (и на клиенте и на сервере) получилось передать файл размером в 75МБ). Мне нужна была это программа для передачи .mp3 файлов, а они много не весят, и с моей «нуждой» программа справлялась отлично. Для передачи больших файлов используйте TFileStream.

Надеюсь, эта статья хоть как-то поможет тебе в разработке приложений. Спасибо за внимание.

Written by: Сергей Дубовик aka sd

Источник: http://www.vr-online.ru/content/peredacha-fajlov-cherez-tcp-na-c-1589

Отправить файл через HTTP POST с помощью C#

Передача файлов c

Я искал и читал вокруг этого и не мог штрафовать ничего действительно полезного.

Я пишу небольшое приложение c# win, которое позволяет пользователю отправлять файлы на веб-сервер не по FTP, а по HTTP с помощью POST. Подумайте об этом как о веб-форме, но работающей в приложении windows.

у меня есть объект HttpWebRequest, созданный с помощью чего-то вроде этого

HttpWebRequest req = WebRequest.Create(uri) as HttpWebRequest

и Method, ContentType и ContentLength свойства. Но это все, что я могу. идти.

Это мой кусок кода:

HttpWebRequest req = WebRequest.Create(uri) as HttpWebRequest;req.KeepAlive = false;req.Method = “POST”;req.Credentials = new NetworkCredential(user.UserName, user.UserPassword);req.PreAuthenticate = true;req.ContentType = file.ContentType;req.ContentLength = file.Length;HttpWebResponse response = null; try{ response = req.GetResponse() as HttpWebResponse;}catch (Exception e) {}

поэтому мой вопрос в основном заключается в том, как я могу отправить fie (текстовый файл, изображение, аудио и т. д.) С C# через HTTP POST.

спасибо!

использование .NET 4.5 (или .NET 4.0 путем добавления Microsoft.Сеть.Http пакет из NuGet) существует более простой способ моделирования запросов формы. Вот пример:

private async Task Upload(string actionUrl, string paramString, Stream paramFileStream, byte [] paramFileBytes){ HttpContent stringContent = new StringContent(paramString); HttpContent fileStreamContent = new StreamContent(paramFileStream); HttpContent bytesContent = new ByteArrayContent(paramFileBytes); using (var client = new HttpClient()) using (var formData = new MultipartFormDataContent()) { formData.Add(stringContent, “param1”, “param1”); formData.Add(fileStreamContent, “file1”, “file1”); formData.Add(bytesContent, “file2”, “file2”); var response = await client.PostAsync(actionUrl, formData); if (!response.IsSuccessStatusCode) { return null; } return await response.Content.ReadAsStreamAsync(); }}

для отправки файла raw только:

using(WebClient client = new WebClient()) { client.UploadFile(address, filePath);}

Если вы хотите эмулировать форму браузера с помощью , тогда это сложнее. См.ответ для ответа multipart / form-data.

для меня client.UploadFile все еще завернул содержимое в многостраничный запрос, поэтому мне пришлось сделать это так:

using (WebClient client = new WebClient()){ client.Headers.Add(“Content-Type”, “application/octet-stream”); using (Stream fileStream = File.OpenRead(filePath)) using (Stream requestStream = client.OpenWrite(new Uri(fileUploadUrl), “POST”)) { fileStream.CopyTo(requestStream); }}

У меня была такая же проблема, и этот следующий код отлично ответил на эту проблему:

//Identificate separatorstring boundary = “—————————” + DateTime.Now.Ticks.ToString(“x”);//Encodingbyte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes(“\r–” + boundary + “\r”); //Creation and specification of the requestHttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url); //sVal is id for the webServicewr.ContentType = “multipart/form-data; boundary=” + boundary;wr.Method = “POST”;wr.KeepAlive = true;wr.Credentials = System.Net.CredentialCache.DefaultCredentials; string sAuthorization = “login:password”;//AUTHENTIFICATION BEGINbyte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(sAuthorization);string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);wr.Headers.Add(“Authorization: Basic ” + returnValue); //AUTHENTIFICATION ENDStream rs = wr.GetRequestStream(); string formdataTemplate = “Content-Disposition: form-data; name=\”{0}\”\r\
{1}”; //For the POST's format //Writting of the filers.Write(boundarybytes, 0, boundarybytes.Length);byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(Server.MapPath(“questions.pdf”));rs.Write(formitembytes, 0, formitembytes.Length); rs.Write(boundarybytes, 0, boundarybytes.Length); string headerTemplate = “Content-Disposition: form-data; name=\”{0}\”; filename=\”{1}\”\rContent-Type: {2}\r\
“;string header = string.Format(headerTemplate, “file”, “questions.pdf”, contentType);byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);rs.Write(headerbytes, 0, headerbytes.Length); FileStream fileStream = new FileStream(Server.MapPath(“questions.pdf”), FileMode.Open, FileAccess.Read);byte[] buffer = new byte[4096];int bytesRead = 0;while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0){ rs.Write(buffer, 0, bytesRead);}fileStream.Close(); byte[] trailer = System.Text.Encoding.ASCII.GetBytes(“\r–” + boundary + “–\r”);rs.Write(trailer, 0, trailer.Length);rs.Close();rs = null; WebResponse wresp = null;try{ //Get the response wresp = wr.GetResponse(); Stream stream2 = wresp.GetResponseStream(); StreamReader reader2 = new StreamReader(stream2); string responseData = reader2.ReadToEnd();}catch (Exception ex){ string s = ex.Message;}finally{ if (wresp != null) { wresp.Close(); wresp = null; } wr = null;}

вам нужно записать свой файл в поток запросов:

using (var reqStream = req.GetRequestStream()) { reqStream.Write( … ) // write the bytes of the file}

для публикации файлов как из байтовых массивов:

private static string UploadFilesToRemoteUrl(string url, IList files, NameValueCollection nvc) { string boundary = “—————————-” + DateTime.Now.Ticks.ToString(“x”); var request = (HttpWebRequest) WebRequest.Create(url); request.ContentType = “multipart/form-data; boundary=” + boundary; request.Method = “POST”; request.KeepAlive = true; var postQueue = new ByteArrayCustomQueue(); var formdataTemplate = “\r–” + boundary + “\rContent-Disposition: form-data; name=\”{0}\”;\r\
{1}”; foreach (string key in nvc.Keys) { var formitem = string.Format(formdataTemplate, key, nvc[key]); var formitembytes = Encoding.UTF8.GetBytes(formitem); postQueue.Write(formitembytes); } var headerTemplate = “\r–” + boundary + “\r” + “Content-Disposition: form-data; name=\”{0}\”; filename=\”{1}\”\r” + “Content-Type: application/zip\r\
“; var i = 0; foreach (var file in files) { var header = string.Format(headerTemplate, “file” + i, “file” + i + “.zip”); var headerbytes = Encoding.UTF8.GetBytes(header); postQueue.Write(headerbytes); postQueue.Write(file); i++; } postQueue.Write(Encoding.UTF8.GetBytes(“\r–” + boundary + “–“)); request.ContentLength = postQueue.Length; using (var requestStream = request.GetRequestStream()) { postQueue.CopyToStream(requestStream); requestStream.Close(); } var webResponse2 = request.GetResponse(); using (var stream2 = webResponse2.GetResponseStream()) using (var reader2 = new StreamReader(stream2)) { var res = reader2.ReadToEnd(); webResponse2.Close(); return res; } } public class ByteArrayCustomQueue { private LinkedList arrays = new LinkedList(); /// /// Writes the specified data. /// /// The data. public void Write(byte[] data) { arrays.AddLast(data); } /// /// Gets the length. /// /// /// The length. /// public int Length { get { return arrays.Sum(x => x.Length); } } /// /// Copies to stream. /// /// The request stream. /// public void CopyToStream(Stream requestStream) { foreach (var array in arrays) { requestStream.Write(array, 0, array.Length); } }} public string SendFile(string filePath) { WebResponse response = null; try { string sWebAddress = “Https://www.address.com”; string boundary = “—————————” + DateTime.Now.Ticks.ToString(“x”); byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes(“\r–” + boundary + “\r”); HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(sWebAddress); wr.ContentType = “multipart/form-data; boundary=” + boundary; wr.Method = “POST”; wr.KeepAlive = true; wr.Credentials = System.Net.CredentialCache.DefaultCredentials; Stream stream = wr.GetRequestStream(); string formdataTemplate = “Content-Disposition: form-data; name=\”{0}\”\r\
{1}”; stream.Write(boundarybytes, 0, boundarybytes.Length); byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(filePath); stream.Write(formitembytes, 0, formitembytes.Length); stream.Write(boundarybytes, 0, boundarybytes.Length); string headerTemplate = “Content-Disposition: form-data; name=\”{0}\”; filename=\”{1}\”\rContent-Type: {2}\r\
“; string header = string.Format(headerTemplate, “file”, Path.GetFileName(filePath), Path.GetExtension(filePath)); byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); stream.Write(headerbytes, 0, headerbytes.Length); FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); byte[] buffer = new byte[4096]; int bytesRead = 0; while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) stream.Write(buffer, 0, bytesRead); fileStream.Close(); byte[] trailer = System.Text.Encoding.ASCII.GetBytes(“\r–” + boundary + “–\r”); stream.Write(trailer, 0, trailer.Length); stream.Close(); response = wr.GetResponse(); Stream responseStream = response.GetResponseStream(); StreamReader streamReader = new StreamReader(responseStream); string responseData = streamReader.ReadToEnd(); return responseData; } catch (Exception ex) { return ex.Message; } finally { if (response != null) response.Close(); } }

Источник: https://askdev.ru/q/otpravit-fayl-cherez-http-post-s-pomoschyu-c-30245/

Передача файлов по UDP

Передача файлов c
120

C# и .NET — Сетевое программирование — Передача файлов по UDP

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

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

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

doc-файл можно открыть программой Microsoft Word, а .html-файл — браузером).

Файловый сервер

Файловый сервер — это простое консольное приложение, реализованное в классе UdpFileServer. В этом классе есть вложенный класс FileDetails, содержащий информацию о файле — тип и размер файла.

Начнем с импорта необходимых пространств имен и объявления полей класса.

В классе есть пять закрытых полей: экземпляр класса FileDetails, объект UdpClient, а также информация о соединении с удаленным клиентом и объект FileStream для считывания файла, который отправляется клиенту:

using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Xml.Serialization; using System.Diagnostics; using System.Threading; public class UdpFileServer { // Информация о файле (требуется для получателя) [Serializable] public class FileDetails { public string FILETYPE = “”; public long FILESIZE = 0; } private static FileDetails fileDet = new FileDetails(); // Поля, связанные с UdpClient private static IPAddress remoteIPAddress; private const int remotePort = 5002; private static UdpClient sender = new UdpClient(); private static IPEndPoint endPoint; // Filestream object private static FileStream fs;

Итак, мы дошли до метода Main() сервера. В этом методе приглашаем пользователя ввести удаленный IP-адрес, по которому нужно отправить файл, путь и имя отправляемого файла. Открываем этот файл в объекте FileStream и определяем его длину.

Если она больше максимально допустимой длины, равной 8192 байтам, закрываем UdpClient и FileStream и выходим из приложения. Иначе отправляем информацию о файле, выжидаем две секунды, вызвав метод Thread.

Sleep(), и отправляем сам файл:

[STAThread] static void Main(string[] args) { try { // Получаем удаленный IP-адрес и создаем IPEndPoint Console.WriteLine(“Введите удаленный IP-адрес”); remoteIPAddress = IPAddress.Parse(Console.ReadLine().ToString());//”127.0.0.1″); endPoint = new IPEndPoint(remoteIPAddress, remotePort); // Получаем путь файла и его размер (должен быть меньше 8kb) Console.WriteLine(“Введите путь к файлу и его имя”); fs = new FileStream(@Console.ReadLine().ToString(), FileMode.Open, FileAccess.Read); if (fs.Length > 8192) { Console.Write(“Файл должен весить меньше 8кБ”); sender.Close(); fs.Close(); return; } // Отправляем информацию о файле SendFileInfo(); // Ждем 2 секунды Thread.Sleep(2000); // Отправляем сам файл SendFile(); Console.ReadLine(); } catch (Exception eR) { Console.WriteLine(eR.ToString()); } }

Метод SendFileInfo() заполняет поля объекта FileDetails, а затем сериализует объект в MemoryStream, используя объект XmlSerializer. Этот объект считывается в массив байтов и передается методу Send() класса UdpClient, который отправляет информацию о файле клиенту:

public static void SendFileInfo() { // Получаем тип и расширение файла fileDet.FILETYPE = fs.Name.Substring((int)fs.Name.Length – 3, 3); // Получаем длину файла fileDet.FILESIZE = fs.Length; XmlSerializer fileSerializer = new XmlSerializer(typeof(FileDetails)); MemoryStream stream = new MemoryStream(); // Сериализуем объект fileSerializer.Serialize(stream, fileDet); // Считываем поток в байты stream.Position = 0; Byte[] bytes = new Byte[stream.Length]; stream.Read(bytes, 0, Convert.ToInt32(stream.Length)); Console.WriteLine(“Отправка деталей файла…”); // Отправляем информацию о файле sender.Send(bytes, bytes.Length, endPoint); stream.Close(); }

Метод SendFile() просто считывает содержимое файла из FileStream в массив байтов и отправляет его клиенту:

private static void SendFile() { // Создаем файловый поток и переводим его в байты Byte[] bytes = new Byte[fs.Length]; fs.Read(bytes, 0, bytes.Length); Console.WriteLine(“Отправка файла размером ” + fs.Length + ” байт”); try { // Отправляем файл sender.Send(bytes, bytes.Length, endPoint); } catch (Exception eR) { Console.WriteLine(eR.ToString()); } finally { // Закрываем соединение и очищаем поток fs.Close(); sender.Close(); } Console.WriteLine(“Файл успешно отправлен.”); Console.Read(); }

Файловый клиент или приемник файла — тоже консольное приложение, реализованное в классе UdpFileClient. Здесь также начинаем с импорта необходимых пространств имен и объявления полей класса:

using System; using System.IO; using System.Net; using System.Diagnostics; using System.Net.Sockets; using System.Text; using System.Xml.Serialization; public class UdpFileClient { // Детали файла [Serializable] public class FileDetails { public string FILETYPE = “”; public long FILESIZE = 0; } private static FileDetails fileDet; // Поля, связанные с UdpClient private static int localPort = 5002; private static UdpClient receivingUdpClient = new UdpClient(localPort); private static IPEndPoint RemoteIpEndPoint = null; private static FileStream fs; private static Byte[] receiveBytes = new Byte[0];

Метод Main() этого приложения только вызывает два метода, чтобы получить, соответственно, информацию о файле и сам файл:

[STAThread] static void Main(string[] args) { // Получаем информацию о файле GetFileDetails(); // Получаем файл ReceiveFile(); }

Метод GetFileDetails() вызывает метод Receive() объекта UdpClient. Тот в свою очередь получает от сервера сериализованный объект FileDetails, который сохраняется в объекте MemoryStream. Для десериализации этого потока в объект FileDetails используем объект XmlSerializer и отображаем полученную информацию на консоли:

private static void GetFileDetails() { try { Console.WriteLine(“———–*******Ожидание информации о файле*******———–“); // Получаем информацию о файле receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint); Console.WriteLine(“—-Информация о файле получена!”); XmlSerializer fileSerializer = new XmlSerializer(typeof(FileDetails)); MemoryStream stream1 = new MemoryStream(); // Считываем информацию о файле stream1.Write(receiveBytes, 0, receiveBytes.Length); stream1.Position = 0; // Вызываем метод Deserialize fileDet = (FileDetails)fileSerializer.Deserialize(stream1); Console.WriteLine(“Получен файл типа .” + fileDet.FILETYPE + ” имеющий размер ” + fileDet.FILESIZE.ToString() + ” байт”); } catch (Exception eR) { Console.WriteLine(eR.ToString()); } }

Метод ReceiveFile() получает файл от сервера и сохраняет его на диске под именем temp, добавляя расширение, извлеченное из объекта FileDetails. Затем вызываем статический метод Process.Start() и открываем документ связанной с расширением программой:

public static void ReceiveFile() { try { Console.WriteLine(“———–*******Ожидайте получение файла*******———–“); // Получаем файл receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint); // Преобразуем и отображаем данные Console.WriteLine(“—-Файл получен…Сохраняем…”); // Создаем временный файл с полученным расширением fs = new FileStream(“temp.” + fileDet.FILETYPE, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); fs.Write(receiveBytes, 0, receiveBytes.Length); Console.WriteLine(“—-Файл сохранен…”); Console.WriteLine(“——-Открытие файла——“); // Открываем файл связанной с ним программой Process.Start(fs.Name); } catch (Exception eR) { Console.WriteLine(eR.ToString()); } finally { fs.Close(); receivingUdpClient.Close(); Console.Read(); } }

Далее показан вывод на консоль на клиенте и сервере, полученный после запуска этой программы:

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

По умолчанию размер буфера равен 8192 байтам, поэтому невозможно отправлять файлы больше 8192 байтов. Это ограничение преодолимо — достаточно разбить файл на несколько частей, не превышающих размер буфера.

Указывая при вызове метода Read() требуемый размер буфера, можно разделить файл на несколько частей.

Протокол UDP не использует сигналов подтверждения, и надо реализовать отдельный механизм, чтобы перед передачей каждой части проверять, корректно ли принята предыдущая часть. Этот механизм можно создать, если построить в отправителе и получателе еще по экземпляру объекта UdpClient, который будет отслеживать подтверждающие сообщения.

Широковещательная передача

При выполнении этого приложения можно указать отдельный IP-адрес, по которому будет отправлен файл, но также можно задать широковещательный адрес, чтобы отправить файл всем машинам подсети или всей сети. Широковещательный адрес состоит из идентификатора подсети и единиц во всех остальных битах.

Например, если хотим разослать сообщение всем хостам в диапазоне 192.168.0, то нужно использовать широковещательный адрес 192.168.0.255. Чтобы отправить сообщение всем машинам сети независимо от маски подсети, мы можем использовать адрес 255.255.255.255.

При широковещательной передаче сообщение отправляется всем машинам сети, а клиенту предстоит решить, хочет ли он обработать эти данные.

Источник: https://professorweb.ru/my/csharp/web/level4/4_8.php

Отправка файлов по сети

Передача файлов c

Отправка файлов по сети имеет определенные особенности по сравнению с отправкой текстовых сообщений. Размер файла в большинстве случаев значительно больше текста и отправку (прием) вследствие этого необходимо осуществлять порциями.

Более того при обмене файлами по сети необходимо кроме содержимого файла передавать информацию об его имени, типе (расширении) и возможно еще какие-либо дополнительные данные.

Сетевой поток байтов в этом случае будет насыщен различной информацией, содержание которой можно узнать только после полной расшифровки.

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

Используя при обмене сетевыми сообщениями принцип работы протокола TCP, разработанный умными людьми для надежной доставки информации, можно успешно обмениваться файлами и текстовыми сообщениями.

Суть способа проста: вся необходимая информация содержится в самом пересылаемом пакете байтов в заранее известном месте.

Подробнее: создается первый (главный заголовок) с информацией о последующих объектах сетевого потока. Главный заголовок имеет фиксированный размер для однозначной его интерпретации.

После заголовка следует информационная структура с подробной информацией о присланных сетевых данных. Размер ее мы и указываем в главном заголовке.

При успешной расшифровке (десериализации) информационной структуры мы получим точные данные, ведь такая структура может нести в себе текстовые, числовые и двоичные данные.

// Метод отправки файлов и других данных по сети public void SendData() { // Состав отсылаемого универсального сообщения // 1. Заголовок о следующим объектом класса подробной информации дальнейших байтов // 2. Объект класса подробной информации о следующих байтах // 3. Байты непосредственно готовых к записи в файл или для чего-то иного. SendInfo si = new SendInfo(); si.message = “текст сообщения”; … FileInfo fi = new FileInfo(SendFileName); si.filesize = (int)fi.Length; si.filename = fi.Name; BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, si); ms.Position = 0; byte[] infobuffer = new byte[ms.Length]; int r = ms.Read(infobuffer, 0, infobuffer.Length); ms.Close(); // байты главного заголовка byte[] header = GetHeader(infobuffer.Length); // Общий массив байтов byte[] total = new byte[header.Length + infobuffer.Length + si.filesize]; Buffer.BlockCopy(header, 0, total, 0, header.Length); Buffer.BlockCopy(infobuffer, 0, total, header.Length, infobuffer.Length); // Добавим содержимое файла в общий массив сетевых данных FileStream fs = new FileStream(SendFileName, FileMode.Open, FileAccess.Read); fs.Read(total, header.Length + infobuffer.Length, si.filesize); fs.Close(); // Отправим данные подключенным клиентам NetworkStream ns = _tcpClient.tcpClient.GetStream(); // Так как данный метод вызывается в отдельном потоке // рациональней использовать синхронный метод отправки ns.Write(total, 0, total.Length); … // Подтверждение успешной отправки Parent.ShowReceiveMessage(“Данные успешно отправлены!”); } // Асинхронный метод приема и расшифровки сетевых данных public void ReadCallback(IAsyncResult ar) { TcpClientData myTcpClient = (TcpClientData)ar.AsyncState; try { NetworkStream ns = myTcpClient.tcpClient.GetStream(); int r = ns.EndRead(ar); if (r > 0) { // Из главного заголовка получим размер массива байтов информационного объекта string header = Encoding.Default.GetString(myTcpClient.buffer); int leninfo = int.Parse(header); // Получим и десериализуем объект с подробной информацией // о содержании получаемого сетевого пакета MemoryStream ms = new MemoryStream(leninfo); byte[] temp = new byte[leninfo]; r = ns.Read(temp, 0, temp.Length); ms.Write(temp, 0, r); BinaryFormatter bf = new BinaryFormatter(); ms.Position = 0; SendInfo sc = (SendInfo)bf.Deserialize(ms); ms.Close(); … // Создадим файл на основе полученной информации и // массива байтов следующих за объектом информации FileStream fs = new FileStream(sc.filename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, sc.filesize); do { temp = new byte[global.MAXBUFFER]; r = ns.Read(temp, 0, temp.Length); // Записываем строго столько байтов сколько прочтено методом Read() fs.Write(temp, 0, r); // Как только получены все байты файла, останавливаем цикл, // иначе он заблокируется в ожидании новых сетевых данных if (fs.Length == sc.filesize) { fs.Close(); break; } } while (r > 0); … if (Receive != null) Receive(this, new ReceiveEventArgs(sc)); … } else { DeleteClient(myTcpClient); // Событие клиент отключился if (Disconnected != null) Disconnected.BeginInvoke(this, “Клиент отключился!”, null, null); } } catch (Exception e) { DeleteClient(myTcpClient); // Событие клиент отключился if (Disconnected != null) Disconnected.BeginInvoke(this, “Клиент отключился аварийно!”, null, null); SoundError(); } }

Исходник построен на классах TcpListener и TcpClient упрощающих процесс создания сетевых приложений. Для сериализации в двоичный формат и обратной десериализации используется класс BinaryFormatter. Ограничений по размеру на пересылаемый файла в исходнике не установлен, максимальный размер зависит от производительности компьютера и объема оперативной памяти.

Среда программирования MS Visual Studio.NET 2010 и выше, открытая среда программирования для C# SharpDevelop 4.2 и выше, .NET Framework 3.0 и выше.

Файл:

Размер:

Загрузки:

tcpsendfiles2_vs11.zip
34 Кбайт
4637

Источник: https://www.interestprograms.ru/source-codes-tcp-otpravka-faylov-po-seti

Обмен данными по сети в C# (протокол TCP) | Стрелец Coder

Передача файлов c
Перейти к содержимому

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

В .NET за работу с TCP отвечают три класса из пространства имён System.Net.Sockets.

  • Socket — обеспечивает базовый функционал TCP и UDP сокетов. В прикладных целях рекомендуется вместо класса Socket использовать классы TcpListener, TcpClient или UdpClient, которые построены на его основе;
  • TcpListener — этот класс обеспечивает функционал TCP сервера;
  • TcpClient – этот класс работает как TCP клиент. С его помощью осуществляется передача данных от клиента серверу и, как ни странно наоборот.

Рассмотрим пример сетевого взаимодействия по протоколу TCP на основе классов TcpListenet и TcpClient.

Клиентская часть

Клиентская часть использует только класс TcpClient.

По протоколу TCP мы можем передавать только двоичные данные. Поэтому после инициализации объекта класса TcpClient нужно подготовить массив байт для чтения и записи двоичных данных, а также открыть поток для передачи.

В примере ниже показан подготовительный этап для передачи данных (в массив байт преобразуется строковое сообщение передаваемое на сервер):

TcpClient client = new TcpClient(address, port); Byte[] data = Encoding.UTF8.GetBytes(outMessage); NetworkStream stream = client.GetStream();

TcpClient client = new TcpClient(address, port);Byte[] data = Encoding.UTF8.GetBytes(outMessage);NetworkStream stream = client.GetStream();

То же самое, но для приёма данных:

TcpClient client = new TcpClient(address, port); Byte[] data = new Byte[256]; NetworkStream stream = client.GetStream();

TcpClient client = new TcpClient(address, port);Byte[] data = new Byte[256];NetworkStream stream = client.GetStream();

Отправка данных на сервер производится при помощи метода Write.

stream.Write(data, 0, data.Length);

stream.Write(data, 0, data.Length);

Первым параметром он принимает оправляемые двоичные данные, второй параметр задаёт сдвиг от начала массива байт (с какого байта начинать передачу, обычно равен нулю (массив байт отправляется целиком)), третий параметр размер двоичных данных (в данном примере массив байт передаётся целиком).

С получением данных гораздо сложнее. Мы не знаем их истинного объёма и потому вынуждены считывать их в цикле до тех пор пока они не закончатся.

Ниже приведён пример получения данных от сервера на примере строки.

string responseData = String.Empty; StringBuilder completeMessage = new StringBuilder(); int numberOytesRead = 0; do { numberOytesRead = stream.Read(readingData, 0,readingData.Length); completeMessage.AppendFormat(“{0}”, Encoding.UTF8.GetString(readingData, 0, numberOytesRead)); } while (stream.DataAvailable); responseData = completeMessage.ToString();

string responseData = String.Empty;StringBuilder completeMessage = new StringBuilder();int numberOytesRead = 0;    numberOytesRead = stream.Read(readingData, 0,readingData.Length);    completeMessage.AppendFormat(“{0}”, Encoding.UTF8.GetString(readingData, 0, numberOytesRead));while (stream.DataAvailable);responseData = completeMessage.ToString();

До тех пор пока в потоке есть данные (свойство DataAvailable равно true) происходит поэтапное формирование поступившей с сервера строки при помощи класса StringBuilder теми данными, что были прочитаны в буфер в ходе текущей итерации. После завершения прочтения всех данных из потока возвращается готовое строковое сообщение.

После завершения передачи или получения поток и сетевое соединение должны быть закрыты.

stream.Close(); client.Close();

Далее представлен пример, который иллюстрирует весь процесс обмена данными с сервером целиком.

private string Exchange(string address, int port, string outMessage) { // Инициализация TcpClient client = new TcpClient(address, port); Byte[] data = Encoding.UTF8.GetBytes(outMessage); NetworkStream stream = client.GetStream(); try { // Отправка сообщения stream.Write(data, 0, data.Length); // Получение ответа Byte[] readingData = new Byte[256]; String responseData = String.Empty; StringBuilder completeMessage = new StringBuilder(); int numberOytesRead = 0; do { numberOytesRead = stream.Read(readingData, 0, readingData.Length); completeMessage.AppendFormat(“{0}”, Encoding.UTF8.GetString(readingData, 0, numberOytesRead)); } while (stream.DataAvailable); responseData = completeMessage.ToString(); return responseData; } finally { stream.Close(); client.Close(); } }

private string Exchange(string address, int port, string outMessage)    TcpClient client = new TcpClient(address, port);    Byte[] data = Encoding.UTF8.GetBytes(outMessage);    NetworkStream stream = client.GetStream();        stream.Write(data, 0, data.Length);        Byte[] readingData = new Byte[256];        String responseData = String.Empty;        StringBuilder completeMessage = new StringBuilder();        int numberOytesRead = 0;            numberOytesRead = stream.Read(readingData, 0, readingData.Length);            completeMessage.AppendFormat(“{0}”, Encoding.UTF8.GetString(readingData, 0, numberOytesRead));        while (stream.DataAvailable);        responseData = completeMessage.ToString();

Серверная часть

Для реализации TCP сервера, на потребуется уже два класса TcpListener (для прослушивания определённого порта и управления подключения клиентов) и TcpClient (для обмена данными с подключенными клиентами).

Конструктор класса TcpListenet принимает два параметра IP адрес и номер порта.

IPAddress localAddr = IPAddress.Parse(“127.0.0.1”); int port = 8888; TcpListener server = new TcpListener(localAddr, port);

IPAddress localAddr = IPAddress.Parse(“127.0.0.1”);TcpListener server = new TcpListener(localAddr, port);

Обратит внимание, что в отличие от TcpClient здесь IP адроес представлен объектом класса IPAddress из пространства имён System.Net.

Запуск сервера в работу осужествляется при помощи метода Start.

После вызова метода Start запускается бесконечный цикл, в котором объект TcpListener будет ожидать подключения клиента и как только оно произойдёт при помощи метода AcceptTcpClient будет создан объект TcpClient, который позволяет производить обмен данными с подключившимся клиентом как это было описано для клиентской части.

TcpClient client = server.AcceptTcpClient();

TcpClient client = server.AcceptTcpClient();

Ниже приведён полный пример реализации TCP сервера, который получает от клиентов строковые сообщения и отправляет им ответы также в виде строки.

// Инициализация IPAddress localAddr = IPAddress.Parse(“127.0.0.1”); int port = 8888; TcpListener server = new TcpListener(localAddr, port); // Запуск в работу server.Start(); // Бесконечный цикл while (true) { try { // Подключение клиента TcpClient client = server.AcceptTcpClient(); NetworkStream stream = client.GetStream(); // Обмен данными try { if (stream.CanRead) { byte[] myReadBuffer = new byte[1024]; StringBuilder myCompleteMessage = new StringBuilder(); int numberOytesRead = 0; do { numberOytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length); myCompleteMessage.AppendFormat(“{0}”, Encoding.UTF8.GetString(myReadBuffer, 0, numberOytesRead)); } while (stream.DataAvailable); Byte[] responseData = Encoding.UTF8.GetBytes(“УСПЕШНО!”); stream.Write(responseData, 0, responseData.Length); } } finally { stream.Close(); client.Close(); } } catch { server.Stop(); break; } }

IPAddress localAddr = IPAddress.Parse(“127.0.0.1”);TcpListener server = new TcpListener(localAddr, port);        TcpClient client = server.AcceptTcpClient();        NetworkStream stream = client.GetStream();                byte[] myReadBuffer = new byte[1024];                StringBuilder myCompleteMessage = new StringBuilder();                int numberOytesRead = 0;                    numberOytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length);                    myCompleteMessage.AppendFormat(“{0}”, Encoding.UTF8.GetString(myReadBuffer, 0, numberOytesRead));                while (stream.DataAvailable);                Byte[] responseData = Encoding.UTF8.GetBytes(“УСПЕШНО!”);                stream.Write(responseData, 0, responseData.Length);

Обратите внимание, что, в отличие от клиентской части, на сервере вначале принимается входящее сообщение и лишь после этого отправляется ответ (обратный порядок действий).

Также стоит обратить внимание на то, что в случае возникновения исключения, работа сервера останавливается и бесконечный цикл завершается, чтобы в случае критической ошибки программа не «зависла».

Сетевое взаимодействие и работа программы

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

Чтобы этого избежать настоятельно рекомендуется работать с сетью в отельном потоке.

Источник: https://streletzcoder.ru/obmen-dannyimi-po-seti-v-c-protokol-tcp/

Поделиться:
Нет комментариев

    Добавить комментарий

    Ваш e-mail не будет опубликован. Все поля обязательны для заполнения.