2012-05-23 10 views
5

Soy nuevo en Scala, por lo que la pregunta puede ser bastante simple, aunque he dedicado un tiempo a intentar resolverla. Tengo un simple servidor TCP Scala (sin actores, hilo sencillo):Problemas con el socket en el simple servidor TCP de Scala

import java.io._ 
import java.net._ 

object Application { 
    def readSocket(socket: Socket): String = { 
    val bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream)) 
    var request = "" 
    var line = "" 
    do { 
     line = bufferedReader.readLine() 
     if (line == null) { 
     println("Stream terminated") 
     return request 
     } 
     request += line + "\n" 
    } while (line != "") 
    request 
    } 

    def writeSocket(socket: Socket, string: String) { 
    val out: PrintWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream)) 
    out.println(string) 
    out.flush() 
    } 

    def main(args: Array[String]) { 
    val port = 8000 
    val serverSocket = new ServerSocket(port) 
    while (true) { 
     val socket = serverSocket.accept() 
     readSocket(socket) 
     writeSocket(socket, "HTTP/1.1 200 OK\r\n\r\nOK") 
     socket.close() 
    } 
    } 
} 

El servidor escucha en localhost:8000 para solicitudes incomming y envía la respuesta HTTP con un solo OK palabra en el cuerpo. Luego ejecuto Apache Benchmark de esta manera:

ab -c 1000 -n 10000 http://localhost:8000/ 

que funciona muy bien por primera vez. La segunda vez que se inicia ab se cuelga producir la siguiente salida en netstat -a | grep 8000:

.... 
tcp  0  0 localhost.localdo:43709 localhost.localdom:8000 FIN_WAIT2 
tcp  0  0 localhost.localdo:43711 localhost.localdom:8000 FIN_WAIT2 
tcp  0  0 localhost.localdo:43717 localhost.localdom:8000 FIN_WAIT2 
tcp  0  0 localhost.localdo:43777 localhost.localdom:8000 FIN_WAIT2 
tcp  0  0 localhost.localdo:43722 localhost.localdom:8000 FIN_WAIT2 
tcp  0  0 localhost.localdo:43725 localhost.localdom:8000 FIN_WAIT2 
tcp6  0  0 [::]:8000    [::]:*     LISTEN  
tcp6  83  0 localhost.localdom:8000 localhost.localdo:43724 CLOSE_WAIT 
tcp6  83  0 localhost.localdom:8000 localhost.localdo:43786 CLOSE_WAIT 
tcp6  1  0 localhost.localdom:8000 localhost.localdo:43679 CLOSE_WAIT 
tcp6  83  0 localhost.localdom:8000 localhost.localdo:43735 CLOSE_WAIT 
tcp6  83  0 localhost.localdom:8000 localhost.localdo:43757 CLOSE_WAIT 
tcp6  83  0 localhost.localdom:8000 localhost.localdo:43754 CLOSE_WAIT 
tcp6  83  0 localhost.localdom:8000 localhost.localdo:43723 CLOSE_WAIT 
.... 

Dado que no más peticiones son atendidas por el servidor. Un detalle más: el mismo script ab con los mismos parámetros funciona sin problemas probando un servidor Node.js simple en la misma máquina. Por lo que este problema no está relacionado con una serie de conexiones TCP abiertas que he puesto para ser reutilizable con

sudo sysctl -w net.ipv4.tcp_tw_recycle=1 
sudo sysctl -w net.ipv4.tcp_tw_reuse=1 

Podría alguien darme una pista sobre lo que me falta?

Editar: Terminación de la corriente de manipulación se ha añadido al código anterior:

if (line == null) { 
     println("Stream terminated") 
     return request 
    } 
+0

CLOSE_WAIT significa que TCP está esperando a que la aplicación para cerrar su zócalo. Otro problema es que no está enviando los terminadores de línea correctos para HTTP. Se especifican como \ r \ n, no \ n. – EJP

+0

He actualizado el código a '\ r \ n' aunque' curl' y 'ab' parecen funcionar bien con' \ n'. En cuanto a CLOSE_WAIT, no parece ser la raíz del problema. Gracias por comentar – nab

+0

CLOSE_WAIT es un síntoma del problema de que la aplicación no ha cerrado el socket. – EJP

Respuesta

2

Estoy publicando la respuesta (parcial) a mi propia pregunta para aquellos que algún día encontrarán el mismo problema. En primer lugar, la naturaleza del problema no radica en el código fuente, sino en el propio sistema que restringe las conexiones numéricas. El problema es que el socket pasó a la función readSocket aparece dañado en algunas condiciones, es decir, no se puede leer y bufferedReader.readLine() devuelve null en la primera llamada o se bloquea indefinidamente. Los siguientes dos pasos hacen que el código de trabajo en algunas máquinas:

  1. aumentar el número de conexiones simultáneas a una toma de corriente con

    sysctl -w net.core.somaxconn=65535 
    
  2. Proporcionar el segundo parámetro para ServerSocket constructor que establecer explícitamente la longitud de cola de conexión:

    val maxQueue = 50000 
    val serverSocket = new ServerSocket(port, maxQueue) 
    

Los pasos anteriores resuelven el problema en las instancias EC2 m1.large, sin embargo, sigo teniendo problemas en mi máquina local.La mejor manera sería utilizar Akka para la materia de ese tipo:

import akka.actor._ 
import java.net.InetSocketAddress 
import akka.util.ByteString 

class TCPServer(port: Int) extends Actor { 

    override def preStart { 
    IOManager(context.system).listen(new InetSocketAddress(port)) 
    } 

    def receive = { 
    case IO.NewClient(server) => 
     server.accept() 
    case IO.Read(rHandle, bytes) => { 
     val byteString = ByteString("HTTP/1.1 200 OK\r\n\r\nOK") 
     rHandle.asSocket.write(byteString) 
     rHandle.close() 
    } 
    } 
} 

object Application { 
    def main(args: Array[String]) { 
    val port = 8000 
    ActorSystem().actorOf(Props(new TCPServer(port))) 
    } 
} 
0

En primer lugar, me gustaría sugerir tratando esto sin ab. Puede hacer algo como:

echo "I'm\nHappy\n" | nc -vv localhost 8000 

En segundo lugar, le sugiero que maneje al final de la secuencia. Aquí es donde BufferedReader.readLine() devuelve nulo. El código anterior solo busca una Cadena vacía. Después de arreglar esto, volvería a intentarlo. Luego prueba con ab, después de que todo se vea bien. Háganos saber si el problema persiste.

+0

Primero, el código funciona en el navegador, curl, telnet, etc. En segundo lugar, el manejo 'nulo' no es la causa raíz. De hecho, en mi situación solo debería ocurrir cuando el cliente cierra la conexión, que no es el caso. He actualizado la pregunta para dejarlo en claro. Gracias por las sugerencias. – nab

Cuestiones relacionadas