Клиент C# для JSON-RPC API

Я пытаюсь отправить запросы к API, который использует JSON-RPC. Я тестировал сервер с Curl как таковой:

> curl -v -d 'SOME_INVALID_REQUEST' http://erbium1.sytes.net:50001
 {"jsonrpc": "2.0", "error": {"code": -32700, "message": "invalid JSON"}, "id": null}

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

Я пытаюсь поговорить с этим сервером, используя C#, но сервер не отвечает. Для отправки сообщений я сделал следующее:

  1. Создал TcpClient
  2. Подключил клиента к серверу (erbium1.sytes.net, 50001)
  3. Перейдите NetworkStream от подключенного клиента
  4. Написал в NetworkStream Когда я отправляю эти сообщения на сервер, функция обратного вызова чтения никогда не вызывается, поэтому я никогда не получаю ответов от сервера. Если бы я отправил сообщения на порт 50002 (TLS) вместо 50001, сервер ответил бы пустой строкой.

Я предполагаю, что выполнение необработанного соединения TcpClient не отправляет сообщения с заголовками HTTP, поэтому сервер просто их отбрасывает? Если да, то как лучше всего запрашивать подписку на сервер и получать уведомления?

В чем будет разница между соединением TcpClient и WebSocket? Подойдет ли здесь WebSocket?

Ниже образец моего кода:

// State object for receiving data from remote device.
public class ReceiverState {
    public ReceiverState(NetworkStream s) {
        this.stream = s;
    }
    public NetworkStream stream;
    public const int bufferSize = 1024;
    public byte[] buffer = new byte[bufferSize];
    public AutoResetEvent receivedEvent = new AutoResetEvent(false);
}



  static AutoResetEvent received = new AutoResetEvent(false);

  public static void PingServer(NetworkStream stream) {
      if (stream.CanWrite) {
          try {
              String rpc = "INVALID_REQUEST";
              Byte[] data = System.Text.Encoding.UTF8.GetBytes(rpc);
              stream.Write(data, 0, data.Length);
              stream.Flush();
              Console.WriteLine("Pinging...");
          } catch (Exception e) {
              Console.WriteLine(e.ToString());
          }
      } else {
          // TODO Throw an exception
          Console.WriteLine("Unable to write to stream");
      }
  }

  public static void BeginAsyncRead(NetworkStream stream, ReceiverState readState) {
      if (stream.CanRead) {
          try {
              stream.BeginRead(readState.buffer, 0, 1024,
                               new AsyncCallback(readCallBack),
                               readState);
          }
          catch (Exception e) {
              Console.WriteLine(e.ToString());
          }
      } else {
          // TODO Throw an exception
          Console.WriteLine("Unable to read from stream");
      }
  }

  private static void readCallBack(System.IAsyncResult ar) {
      // Get the state from AsyncResult
      ReceiverState state = (ReceiverState) ar.AsyncState;

      if (ar.IsCompleted){
          Int32 numBytes = state.stream.EndRead(ar);
          Console.WriteLine("Received : ");
          Console.WriteLine(Encoding.ASCII.GetString(state.buffer,0,numBytes+10));
          state.receivedEvent.Set();
      }
  }

  private static void synchronousRead(NetworkStream myNetworkStream) {
      if (myNetworkStream.CanRead){
          byte[] myReadBuffer = new byte[1024];
          StringBuilder myCompleteMessage = new StringBuilder();
          int numberOfBytesRead = 0;

          // Incoming message may be larger than the buffer size.
          do{
              numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
              myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
          }
          while(myNetworkStream.DataAvailable);
          // Print out the received message to the console.
          Console.WriteLine("You received the following message : " +
                            myCompleteMessage);
      }
      else{
          Console.WriteLine("Sorry.  You cannot read from this NetworkStream.");
      }
  }

  public static void Main(string[] args) {
    // Create a new TcpClient
    TcpClient client = new TcpClient();
    Console.WriteLine("Connecting...");
    client.Connect("electrum3.hachre.de", 50001);
    NetworkStream stream = client.GetStream();
    Stream inStream = new MemoryStream();
    ReceiverState readState = new ReceiverState(stream);
    readState.receivedEvent = received;
    BeginAsyncRead(stream, readState);
    PingServer(stream);
    received.WaitOne();
  }

TCP - это транспортный уровень для HTTP. TCP отправляет только необработанные данные, и вы должны добавить любые заголовки http. Сетевой веб-сокет добавит заголовки по умолчанию. Вы можете многому научиться, используя сниффер, такой как wirehark, который выдаст вам все отправляемые IP-сообщения.

jdweng 09.09.2018 22:17

@jdweng, насколько я понимаю, можно установить постоянное соединение через веб-сокет, если да, будет ли каждый отправленный запрос иметь соответствующие заголовки? Или только начальное рукопожатие для установки веб-сокета? Ожидают ли веб-API по умолчанию, что сообщения будут получены с заголовками HTTP по умолчанию?

omzy 09.09.2018 23:05

TCP-соединение остается открытым между запросами. Каждое HTTP-сообщение имеет заголовок, но исходный заголовок может иметь больше параметров, чем последующие сообщения. См. Wiki: Веб-API en.wikipedia.org/wiki/Hypertext_Transfer_Protocol могут использовать любой IP-протокол. Я думаю, что в этом случае Web относится к любому IP-протоколу, а не только к HTTP. Большинство веб-API используют http. Но некоторые из них - это SMTP (электронная почта) и FTP (передача файлов), которые являются расширенными формами http. У вас может быть TELNET API, который представляет собой простые приложения чата TCP.

jdweng 10.09.2018 00:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
1 280
0

Другие вопросы по теме