[英]Socket issues in simple Scala TCP server
我是Scala的新手,所以问题可能很简单,尽管我花了一些时间来解决它。 我有一个简单的Scala TCP服务器(没有actor,单线程):
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()
}
}
}
服务器侦听localhost:8000
以获取请求,并在正文中发送带有单个OK
单词的HTTP响应。 然后我像这样运行Apache Benchmark:
ab -c 1000 -n 10000 http://localhost:8000/
这是第一次很好用。 第二次我开始ab
它挂起在netstat -a | grep 8000
产生以下输出 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
....
由于服务器不再提供请求。 还有一个细节:具有相同参数的相同ab
脚本可以在同一台机器上平稳地测试一个简单的Node.js服务器。 所以这个问题与我设置为可重用的多个已打开的TCP连接无关
sudo sysctl -w net.ipv4.tcp_tw_recycle=1
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
谁能给我一个我错过的线索?
编辑 :流处理的终止已添加到上面的代码中:
if (line == null) {
println("Stream terminated")
return request
}
对于那些偶尔会遇到同样问题的人,我发布了我自己问题的(部分)答案。 首先,问题的本质不在于源代码,而在于系统本身限制了数字连接。 问题是传递给readSocket
函数的socket
在某些条件下会出现损坏,即无法读取它, bufferedReader.readLine()
在第一次调用时返回null
或无限期挂起。 以下两个步骤使代码在某些机器上运行:
增加与套接字的并发连接数
sysctl -w net.core.somaxconn=65535
向ServerSocket
构造函数提供第二个参数,该构造函数将显式设置连接队列的长度:
val maxQueue = 50000 val serverSocket = new ServerSocket(port, maxQueue)
上面的步骤解决了EC2 m1.large实例上的问题,但是我的本地机器上仍然遇到问题。 更好的方法是使用Akka来做这类事情:
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)))
}
}
首先,我建议不用ab
尝试。 你可以这样做:
echo "I'm\nHappy\n" | nc -vv localhost 8000
其次,我建议处理流末尾。 这是BufferedReader.readLine()
返回null的地方。 上面的代码只检查一个空字符串。 解决之后,我再试一次。 一切看起来都不错,然后用ab测试。 如果问题仍然存在,请告诉我们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.