套接字连接使用过多 CPU
Excessive CPU usage upon socket connection
我有一个客户端 class,它允许我连接到 ntrip 客户端服务(它基本上是一个 http 服务)。
该客户端允许我连接到安全和非安全服务。我正在使用 javax.net
库。
这是我的代码:
public class ntripClient_mng implements Runnable{
private static String nServer = "";
private static String nMountpoint = "";
private static String nUsername = "";
private static String nPassword = "";
private static int nPort = 0;
private boolean running = true;
private static boolean secure = false;
public static void main(String[] args){
for (int i = 0; i < args.length; i++){
if (args[i].equals("-a")){nServer = args[i+1];}
if (args[i].equals("-p")){nPort = Integer.parseInt(args[i+1]);}
if (args[i].equals("-u")){nUsername = args[i+1];}
if (args[i].equals("-pw")){nPassword = args[i+1];}
if (args[i].equals("-m")){nMountpoint = args[i+1];}
if (args[i].equals("-s")){secure = args[i+1].matches("Y") ? true : false;}
}
public ntripClient_mng(String server, int port, String user, String pass, String mount, String cType){
nServer = server;
nUsername = user;
nPassword = pass;
nMountpoint = mount;
nPort = port;
secure = cType.matches("Y") ? true : false;
}
@Override
public void run() {
DataOutputStream out = null;
DataInputStream in = null;
try {
Socket s = null;
SSLSocket sslSocket = null;
// Creating Client Sockets
if (secure){
SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
sslSocket = (SSLSocket)sslsocketfactory.createSocket(nServer,nPort);
} else {
SocketAddress sockaddr = new InetSocketAddress(nServer, nPort);
s = new Socket();
s.connect(sockaddr, 10 * 1000);
}
if (true) {
if (secure){
sslSocket.setSoTimeout(15000);
out = new DataOutputStream (sslSocket.getOutputStream());
in = new DataInputStream (sslSocket.getInputStream());
} else {
s.setSoTimeout(15000);
out = new DataOutputStream (s.getOutputStream());
in = new DataInputStream (s.getInputStream());
}
// send a message to the server
String requestmsg = GETrequest();
out.write(requestmsg.getBytes());
out.flush();
while (running) {
// receive a response
byte[] b = new byte[1];
if (first){
for (;;){
in.read(b);
if (b[0] == -45) break;
}
baos.write(b);
first = false;
}
for (;;){
in.read(b);
if (b[0] == -45){
break;
} else {
baos.write(b);
}
}
byte[] bytes = baos.toByteArray();
decodeMessage(bytes);
baos.reset();
baos.write(b);
}
}
}
catch (UnknownHostException ex) {ex.printStackTrace();}
catch (IOException ex) {ex.printStackTrace();}
}}
该代码对于安全连接和非安全连接都运行良好。然而,困扰我的是在连接到非安全服务时过度使用 CPU。
当我连接到安全服务时,一个 运行 线程的 CPU 使用率约为 0.3%。当我连接到一个非安全的时,CPU 使用率约为 30%。
显然这里有问题,但我不明白它是什么。
编辑
我已经根据答案中的建议编辑了代码。
现在我正在读取数据,一次一个字节,并使用一个停止字节来停止读取。
这可能是正确的做法,但问题仍然存在。
在连接到某些服务时,CUP 使用率达到 30%。
可能是什么问题? javax.net
库是否有问题?问题可能是由服务器本身引起的吗?
任何帮助,将不胜感激。
// send a message to the server
while (running)
{
// receive a response
if (secure)
{
// ... some code here
}
else
{
// !CHANGES HERE!
byte[] bytes = readTillTimeout(in);
decodeMessage(bytes);
}
}
我通常只是逐字节读取,然后使用 ByteArrayOutputStream
.
将它们相加
当没有更多可用字节时,读取方法将锁定一小段时间(您指定的setSoTimeout(...)
)。如果在该延迟内没有收到任何内容,那么它将抛出一个 SocketTimeoutException
,这意味着您的数据已到达末尾。
private byte[] readTillTimeout(DataInputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try
{
int input = in.read();
if (input == -1) throw new IllegalStateException("disconnected");
baos.write((byte)input);
}
catch (SocketTimeoutException ste)
{
}
return baos.toByteArray();
}
我认为 setSoTimeout(value)
真的不应该超过 2 秒。
我在许多应用程序中使用了这种方法。
我刚刚用您指定的代码对此进行了测试。就像你说的,之前是 30% CPU 使用率。现在大约是 ~0%。
编辑:(因为 EJP 否决了我的回答)
我们今天的听众很难。
是的,超时确实不总是一个好的解决方案。例如,有时您想阅读直到找到特定字符。与工业机器通信的一种非常流行的方法是使每条消息以 STX
(0x02) 字节开始并以 ETX
(0x03) 字节结束。
这是一个使用 startByte
和 stopByte
输入的代码示例。
private byte[] readMessage(DataInputStream in, byte startByte, byte stopByte) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try
{
for(;;)
{
int input = in.read();
if (input == -1) throw new IllegalStateException("disconnected");
if (input == startByte) break;
}
for(;;)
{
int input = in.read();
if (input == -1) throw new IllegalStateException("disconnected");
if (input == stopByte) break;
baos.write((byte)input);
}
}
catch (SocketTimeoutException ste)
{
return null;
}
return baos.toByteArray();
}
我有一个客户端 class,它允许我连接到 ntrip 客户端服务(它基本上是一个 http 服务)。
该客户端允许我连接到安全和非安全服务。我正在使用 javax.net
库。
这是我的代码:
public class ntripClient_mng implements Runnable{
private static String nServer = "";
private static String nMountpoint = "";
private static String nUsername = "";
private static String nPassword = "";
private static int nPort = 0;
private boolean running = true;
private static boolean secure = false;
public static void main(String[] args){
for (int i = 0; i < args.length; i++){
if (args[i].equals("-a")){nServer = args[i+1];}
if (args[i].equals("-p")){nPort = Integer.parseInt(args[i+1]);}
if (args[i].equals("-u")){nUsername = args[i+1];}
if (args[i].equals("-pw")){nPassword = args[i+1];}
if (args[i].equals("-m")){nMountpoint = args[i+1];}
if (args[i].equals("-s")){secure = args[i+1].matches("Y") ? true : false;}
}
public ntripClient_mng(String server, int port, String user, String pass, String mount, String cType){
nServer = server;
nUsername = user;
nPassword = pass;
nMountpoint = mount;
nPort = port;
secure = cType.matches("Y") ? true : false;
}
@Override
public void run() {
DataOutputStream out = null;
DataInputStream in = null;
try {
Socket s = null;
SSLSocket sslSocket = null;
// Creating Client Sockets
if (secure){
SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
sslSocket = (SSLSocket)sslsocketfactory.createSocket(nServer,nPort);
} else {
SocketAddress sockaddr = new InetSocketAddress(nServer, nPort);
s = new Socket();
s.connect(sockaddr, 10 * 1000);
}
if (true) {
if (secure){
sslSocket.setSoTimeout(15000);
out = new DataOutputStream (sslSocket.getOutputStream());
in = new DataInputStream (sslSocket.getInputStream());
} else {
s.setSoTimeout(15000);
out = new DataOutputStream (s.getOutputStream());
in = new DataInputStream (s.getInputStream());
}
// send a message to the server
String requestmsg = GETrequest();
out.write(requestmsg.getBytes());
out.flush();
while (running) {
// receive a response
byte[] b = new byte[1];
if (first){
for (;;){
in.read(b);
if (b[0] == -45) break;
}
baos.write(b);
first = false;
}
for (;;){
in.read(b);
if (b[0] == -45){
break;
} else {
baos.write(b);
}
}
byte[] bytes = baos.toByteArray();
decodeMessage(bytes);
baos.reset();
baos.write(b);
}
}
}
catch (UnknownHostException ex) {ex.printStackTrace();}
catch (IOException ex) {ex.printStackTrace();}
}}
该代码对于安全连接和非安全连接都运行良好。然而,困扰我的是在连接到非安全服务时过度使用 CPU。 当我连接到安全服务时,一个 运行 线程的 CPU 使用率约为 0.3%。当我连接到一个非安全的时,CPU 使用率约为 30%。 显然这里有问题,但我不明白它是什么。
编辑
我已经根据答案中的建议编辑了代码。 现在我正在读取数据,一次一个字节,并使用一个停止字节来停止读取。 这可能是正确的做法,但问题仍然存在。 在连接到某些服务时,CUP 使用率达到 30%。
可能是什么问题? javax.net
库是否有问题?问题可能是由服务器本身引起的吗?
任何帮助,将不胜感激。
// send a message to the server
while (running)
{
// receive a response
if (secure)
{
// ... some code here
}
else
{
// !CHANGES HERE!
byte[] bytes = readTillTimeout(in);
decodeMessage(bytes);
}
}
我通常只是逐字节读取,然后使用 ByteArrayOutputStream
.
当没有更多可用字节时,读取方法将锁定一小段时间(您指定的setSoTimeout(...)
)。如果在该延迟内没有收到任何内容,那么它将抛出一个 SocketTimeoutException
,这意味着您的数据已到达末尾。
private byte[] readTillTimeout(DataInputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try
{
int input = in.read();
if (input == -1) throw new IllegalStateException("disconnected");
baos.write((byte)input);
}
catch (SocketTimeoutException ste)
{
}
return baos.toByteArray();
}
我认为 setSoTimeout(value)
真的不应该超过 2 秒。
我在许多应用程序中使用了这种方法。 我刚刚用您指定的代码对此进行了测试。就像你说的,之前是 30% CPU 使用率。现在大约是 ~0%。
编辑:(因为 EJP 否决了我的回答)
我们今天的听众很难。
是的,超时确实不总是一个好的解决方案。例如,有时您想阅读直到找到特定字符。与工业机器通信的一种非常流行的方法是使每条消息以 STX
(0x02) 字节开始并以 ETX
(0x03) 字节结束。
这是一个使用 startByte
和 stopByte
输入的代码示例。
private byte[] readMessage(DataInputStream in, byte startByte, byte stopByte) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try
{
for(;;)
{
int input = in.read();
if (input == -1) throw new IllegalStateException("disconnected");
if (input == startByte) break;
}
for(;;)
{
int input = in.read();
if (input == -1) throw new IllegalStateException("disconnected");
if (input == stopByte) break;
baos.write((byte)input);
}
}
catch (SocketTimeoutException ste)
{
return null;
}
return baos.toByteArray();
}