Как приложение C # может легко общаться и передавать файлы по сети? - PullRequest
6 голосов
/ 09 августа 2009

Как приложение C # может легко взаимодействовать с собственным экземпляром, присутствующим на другом компьютере в той же сети, и передавать файлы и данные?

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

Я слышал, что сервис "Bonjour" от Apple был хорошим протоколом. Можем ли мы общаться через него из наших приложений для Windows? Или вы должны использовать «розетки». В первую очередь я ищу библиотеки или примеры кода, которые могут легко удовлетворить мои потребности, я не хочу разрабатывать свой собственный протокол на основе TCP или что-то хардкорное!

Ответы [ 4 ]

5 голосов
/ 09 августа 2009

Вы можете использовать System.Net.Sockets класс для связи, и у него есть метод для отправки файла Socket.SendFile .

Обновление:
это хороший пример для совместного использования файлов и отправки файлов из справки C #

2 голосов
/ 09 августа 2009

Отличительной особенностью файлов и сокетов в C # является то, что они оба отображаются в виде потоков. Копировать большой файл из одного потока в другой довольно просто:

byte[] data = new byte[1024];
while(true) {
int bytesRead = filestream.read(data,0,data.Length);
if (bytesRead==0) break;
netstream.write(data,0,bytesRead);
}

Тогда просто закройте гнездо, когда закончите.

Если вы хотите отправить метаданные (имена файлов, размеры) или не хотите закрывать соединение, вам нужен какой-то протокол для обработки этого. FTP использует два отдельных сокета (один для метаданных, один для данных; это называется внеполосной связью). Если вы находитесь в локальной сети без брандмауэров, это может быть вполне приемлемым. С другой стороны, если вы хотите осуществлять передачу через Интернет, открыть один порт достаточно сложно, а два - невыносимо. Если вас не слишком заботит производительность, вы можете закодировать байты в кодировке base64, что гарантирует, что они находятся в определенном диапазоне байтов. С base64 вы можете разделять сообщения символами новой строки или другими не алфавитно-цифровыми символами. Затем в первом сообщении укажите имя файла, размер или что-то еще, затем отправьте данные как второе сообщение, а затем отправьте сообщение «вот и весь файл», чтобы клиент знал, что это сделано.

Еще одна тактика для сообщений - использование escape-последовательности. Например, возьмите свой поток сообщений и замените каждый экземпляр '\ 0' на '\ 0 \ 0'. Теперь используйте '\ 0 \ 1' для обозначения конца сообщения, которое гарантированно не будет содержаться в вашем сообщении данных. Декодируйте '\ 0 \ 0' обратно в '\ 0' на принимающей стороне. Это работает достаточно хорошо в C, но я обнаружил, что на практике циклический просмотр каждого байта может быть медленнее, чем чтение целых буферов в C #.

Лучший способ - использовать протокол с адаптивной длиной. Например, отправьте данные кусками определенного размера (скажем, 512 байт). Перед каждым чанком отправьте 32-битное целое число, представляющее размер чанка, через System.BitConverter. Поэтому сообщения выглядят так (на английском):

Here's 512 bytes:
[data]
Here's 512 bytes:
[data]
Here's 32 bytes:
[data]
Here's 4 bytes:
That was the whole file

Преимущество здесь в том, что вы можете заставить буферы копирования / чтения работать на вас (считывая 512 байт за раз), то есть ваша пропускная способность ограничена вашим сетевым стеком, а не кодом C #. Клиент читает 32-битное int фиксированной длины, которое позволяет ему узнать размер буфера, который он должен использовать для следующего сегмента [data].

Вот код для написания таких сообщений:

        logger.logger.debug("Sending message of length " + length);
        byte[] clength = System.BitConverter.GetBytes(buffer.Length);
        plaintextStream.Write(clength,0,clength.Length);
        plaintextStream.Write(buffer,0,buffer.Length);
        plaintextStream.Flush();

И вот код для их чтения:

               byte[] intbuf = new byte[int_32_size];
        int offset = 0;
        while (offset < int_32_size)
        {
            int read = 0;

            read = d.plaintextStream.Read(intbuf,offset,int_32_size - offset);
            offset += read;

        }
        int msg_size = System.BitConverter.ToInt32(intbuf,0);
        //allocate a new buffer to fill the message
        byte[] msg_buffer = new byte[msg_size];
        offset = 0;
        while (offset < msg_size)
        {
            int read = 0;

            read = d.plaintextStream.Read(msg_buffer,offset,msg_size - offset);
            offset += read;
        }


        return msg_buffer;
2 голосов
/ 09 августа 2009

Для передачи файлов / данных вы можете использовать классы TcpClient / TcpListener , которые являются хорошими абстракциями над функциональностью сокет более грубого типа. Или вы можете просто использовать приложение в качестве HTTP-сервера, используя класс HttpListener , если это проще / более подходит для вашего приложения.

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

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

1 голос
/ 08 февраля 2012

Для копирования файлов вы также можете обратиться к поставщику синхронизации файлов, который является частью Microsoft Sync Framework. http://msdn.microsoft.com/en-us/sync/bb887623.

...