В поисках альтернатив C++

В связи со своим вынужденным переходом в разработчики Java я получил довольно интересную возможность посмотреть на C и C++ со стороны. В целом, мне нравится то что я вижу глядя на C++ с позиции Java разработчика, но, само собой, нашлось одно «но», которое, если быть честным, мне и раньше не давало покоя: C++ совершенно не подходит для быстрого прототипирования. По большому счету это единственный недостаток который я отмечаю на данный момент.

Если говорить про Java, то с быстрым прототипированием тут все нормально, в то время как скорость разработки приложений приблизительно такая же•• как и в случае с C++, ну может на 10% быстрее. При этом язык крайне ограниченный, практически до убогости, что после 10 лет разработки на C++ вызывает довольно ощутимое отторжение.

Понимая, что так дело не пойдет, я взялся за поиск приличной альтернативы C++. Изначально подумывал про Erlang, ведь в основном я пишу что-то сетевое, но в итоге от этого варианта решил отказаться, потому как за пределами сетей Erlang мягко говоря бесполезен, а в самих сетях, зачастую, ограничен в плане набора доступных библиотек. В результате довольно долгих поисков выбор остановился на JVM, ведь JVM — это действительно кроссплатформенное решение (в отличие от того же CLR) с большой базой библиотек под нормальными лицензиями (MIT, BSD и, в худшем случае, LGPL). Плюс, в JDK, начиная с 7-й версии, появилась поддержка асинхронного ввода/вывода, т.е. возникла вполне реальная возможность писать не тормозящие где попало сетевые приложения.

К основным языкам базирующимся на JVM, само собой за исключением самой Java, относятся Clojure, Groovy и Scala. Clojure — Lisp-подобный язык, так что отмел его сразу. Да, я к Lisp отношусь хорошо, но вот куча народу вокруг — нет, так что никакой возможности реально использовать что-то Lisp-подобное я не вижу. Groovy — хороший язык, но без чего-то действительно цепляющего. А вот Scala… Да, Scala — это язык с довольно крутой кривой вхождения, при этом богатым синтаксисом и отличными возможностями.

У всех разные способы оценить пригодность языка для своих нужд. Кто-то первым делом реализует быструю сортировку, кто-то пишет Hello World. А я, обычно, пишу Echo-сервер с клиентом.В данном посте я начну просто с исходников сервера с клиентом, а позже распишу что, как и почему. Хорошо знающим Scala, конечно, посчитают это скучным, но вот тем кто только думает не выучить ли этот замечательный язык, полагаю, будет интересно. Как мне кажется на данный момент, само собой, почти наверняка это не так, в коде присутствуют основные интересные фичи языка Scala.

Клиент и сервер построены на самой примитивной и не желательной к использованию в продакшн концепции: блокирующий ввод/вывод, одно соединение — один поток.

Echo-сервер.

import actors.Actor
import java.io.{InputStreamReader, BufferedReader, InputStream, OutputStream, PrintWriter, OutputStreamWriter}
import java.net.{SocketException, ServerSocket, Socket}
import util.logging.ConsoleLogger

case class Client(socket: Socket);

class Worker extends Actor with ConsoleLogger {

  implicit def inputStreamWrapper(in: InputStream): BufferedReader =
    new BufferedReader(new InputStreamReader(in))

  implicit def outputStreamWrapper(out: OutputStream): PrintWriter =
    new PrintWriter(new OutputStreamWriter(out))

  def act {
    loop {
      react {
        case Client(socket) => {
          log("New remote connection from " + socket.getRemoteSocketAddress )
          log(Thread.currentThread() + "Curent thread ")
          handle(socket.getInputStream, socket.getOutputStream)
          socket.close()
          log("Remote connection was closed")
          exit()
        }
      }
    }
  }

  private def handle(reader : BufferedReader, writer : PrintWriter) {
    var line = reader.readLine()
    while(line != null) {
      writer.println(line)
      writer.flush()
      line = reader.readLine()
    }
  }
}

object EchoServer extends ConsoleLogger {

  def main(args: Array[String]) {

    var serverPort = 8088

    def parseArgs(argsList : List[String]) {
      argsList match {
        case "--port" :: value ::tail =>
          serverPort = value.toInt
          parseArgs(tail)
        case "--help" :: value :: tail =>
          println("usage: EchoServerIO [--port port_number]")
          sys.exit()
        case option :: tail =>
          println("Unknown option")
          sys.exit()
        case Nil => ()
      }
    }
    parseArgs(args.toList)

    try {
      val serverSocket = new ServerSocket( serverPort )
      log("Waiting for connections on " + serverSocket.getLocalSocketAddress)

      while(true) {
        val clientConnection = serverSocket.accept()
        val worker = new Worker
        worker.start()
        worker ! Client( clientConnection )
      }
    } catch {
      case sockErr: SocketException =>
        log("Network error: " + sockErr)
      case generalErr : Exception =>
        log("General error: " + generalErr)
    }
  }
}

Echo-клиент.

import actors.Actor
import java.io.{OutputStreamWriter, InputStreamReader, BufferedReader, PrintWriter}
import java.net.{Socket}
import scala.util.control.Breaks._
import util.logging.ConsoleLogger
import util.Random

case class DestAddress(addr : String,  port : Int, msgCount : Int);

class EchoClientImpl extends Actor with ConsoleLogger {
  val outMsg = Random.nextString(100)

  private def exchange(msgCount : Int, in : BufferedReader, out : PrintWriter) {
    breakable {
      for ( i <- 0 until msgCount ) {
        out.println(outMsg)
        out.flush()
        val line = in.readLine()
        if (outMsg != line) {
          log("Invalid output string.")
          break
        }
      }
    }
  }

  def act() {
    loop {
      react {
        case DestAddress(addr, port, msgCount) => {
          log("Establishing new connection to " + addr + ":" + port)
          try {
            val socket = new Socket(addr, port)
            val in = new BufferedReader(new InputStreamReader(socket.getInputStream))
            val out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream))
            exchange(msgCount, in, out)
            socket.close()
          } catch {
            case err : Exception => println("Error: " + err)
          }
          log("Done.")
          exit()
        }
      }
    }
  }
}

object EchoClient extends ConsoleLogger {
  def main(args: Array[String]) {

    var serverPort = 8088
    var serverAddr = "127.0.0.1"
    var connectionsCount = 16
    var msgCount = 100

    def parseArgs(argsList : List[String]) {
      argsList match {
        case "--addr" :: value ::tail =>
          val address = """(.+):(d+)""".r
          value match {
            case address(ip, port) => {
              serverAddr = ip
              serverPort = port.toInt
            }
          }
          parseArgs(tail)
        case "--connections" :: value ::tail =>
          connectionsCount = value.toInt
          parseArgs(tail)
        case "--help" :: value :: tail =>
          println("usage: EchoClientIO [--addr ip_address:port] [--connections connections_count]" +
                  " [--messages messages_count]")
          sys.exit()
        case option :: tail =>
          println("Unknown option")
          sys.exit()
        case Nil => ()
      }
    }
    parseArgs(args.toList)

    for( i <- 0 until connectionsCount) {
      log("Client Nr" + i + " was started.")
      val client = new EchoClientImpl
      client.start()
      client ! new DestAddress(serverAddr, serverPort, msgCount)
    }
  }
}

3 Comments В поисках альтернатив C++

  1. Andy La Rubin

    Если остановился на JVM, посмотри на Kotlin (если он зарелизился). Лучшие собаководы вроде рекомендовали (при этом критиковали скалу за время компиляции

    Reply
    1. Alexander Stavonin

      Кстати да, надо и на него взглянуть. Спасибо что напомнил!
      Кстати, еще есть Ceylon от КраснойШапочки.

      UPD. Поглядел на оба. Первый (Kotlin) – люди явно не осилили Scala, что не удивительно, он довольно тяжелый для вхождений. Второй (Ceylon) – попытка создать продвинутую версию Java с проблемами с совместимостью с реальной Java. Вобщем, не торт.
      А в Scala мне очень нравится то, что это эдакий Erlang на JVM.

      Reply
  2. Pingback: Слегка разочаровался | System Development

Leave a Reply to Andy La Rubin Cancel reply