需求是用户通过一个外网的ip和端口发送数据,我需要在服务器端接收并保存下来
第一个坑是外网端口对应的内网端口3369需要自己用nginx配置,之前用项目的nginx的配置模仿着写了一下,但是服务器卡在accept那里,好像是没有接收到请求,所以觉得应该是nginx转发请求有问题,可能配置出错了,后来运维帮忙改了下配置,的确似乎可以,虽然在报错,但是客户发送到请求好像能看到。。报错信息如下:
java.lang.IllegalArgumentException: Invalid character found in method name [{"ygdd":226169.99999999997,"deviceid":10000000000300,"timestamp":1670407934565}...]. HTTP method names must be tokens
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:419) ~[tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:271) ~[tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) [tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) [tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.65.jar!/:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.65.jar!/:na]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_201]
一开始我的配置是http的配置:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
client_max_body_size 30m;
server {
listen 3369;
server_name localhost;
location /energy{
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 5000;
proxy_send_timeout 5000;
proxy_read_timeout 5000;
proxy_pass http://192.168.XX.XX:8803;
}
}
}
这个是http请求的配置,但其实我应该是tcp请求,所以这个配置根本就不行
别人修改的配置:
stream {
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
upstream cloudsocket {
hash $remote_addr consistent;
server 192.168.XX.XX:8803 weight=5 max_fails=3 fail_timeout=30s;
}
server {
error_log logs/error_3369.log;
access_log logs/access_3369.log proxy;
listen 3369;
proxy_connect_timeout 10s;
proxy_timeout 300s;
proxy_pass cloudsocket;
}
}
这个是处理tcp请求的,网上很多介绍tcp请求和http请求的,我到现在都很懵,怎么判断是客户端请求是tcp请求还是http请求呢。。之后再找吧,懒得管了。。
然后就是代码部分,逻辑是,外网ip加外网端口3369发送请求,外网端口3369对应的内网端口也是3369,nginx里配置了对3369的监听,会转发请求到远程的远程服务器,也就是192.168.XX.XX:8803,这个8803一定不要误会,不是要指定的tomcat的端口,是最后要监听的端口,服务器端的代码里要监听的是8803,而不是3369。。这是一直卡在accept的原因,因为压根的确没收到请求。。
server端:
package energy_use;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static final int port = 8803;
// public static final String host = "139.198.177.XX";
public static void main(String[] args) {
System.out.println("Server...\n");
Server server = new Server();
server.init();
}
public void init() {
try {
//创建一个ServerSocket,这里可以指定连接请求的队列长度
//new ServerSocket(port,3);意味着当队列中有3个连接请求是,如果Client再请求连接,就会被Server拒绝
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("00000");
while (true) {
System.out.println("010101010101");
//从请求队列中取出一个连接
Socket client = serverSocket.accept();
System.out.println("0");
// 处理这次连接
new HandlerThread(client);
}
} catch (Exception e) {
System.out.println("服务器异常: " + e.getMessage());
}
}
private class HandlerThread implements Runnable {
private Socket socket;
public HandlerThread(Socket client) {
System.out.println("111111111111111");
socket = client;
new Thread(this).start();
}
public void run() {
try {
// 读取客户端数据
System.out.println("222222222");
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("333333333333");
String clientInputStr = input.readLine();//这里要注意和客户端输出流的写方法对应,否则会抛 EOFException
// 处理客户端数据
System.out.println("客户端发过来的内容:" + clientInputStr);
// 向客户端回复信息
PrintStream out = new PrintStream(socket.getOutputStream());
// System.out.print("请输入:\t");
// // 发送键盘输入的一行
// String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
out.println("OK");
out.close();
input.close();
} catch (Exception e) {
System.out.println("服务器 run 异常: " + e.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
socket = null;
System.out.println("服务端 finally 异常:" + e.getMessage());
}
}
}
}
}
}
client:
package energy_use;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
public class Client {
public static final int port = 3369;
public static final String host = "139.198.177.XX";
// public static final String host = "127.0.0.1";
public static void main(String[] args) throws Exception {
while(true) {
Socket socket = new Socket(host,port);
// sock.connect(addr);
// SocketAddress addr = new InetSocketAddress(host,port);
// Socket socket = new Socket();
// socket.connect(addr);
//写数据
OutputStream os =socket.getOutputStream();
// os.write("GET / HTTP/1.1\r\n".getBytes());
// os.write(("Host: " + "139.198.177.221\r\n").getBytes());
// os.write("Content-Type: application/x-www-form-urlencoded\r\n".getBytes());
// //保持连接
// os.write("Connection: keep-alive\r\n".getBytes());
// os.write("Content-Length: 3\r\n".getBytes());
// os.write("\r\n".getBytes());
//
os.write("123\r\n".getBytes());
os.flush();
//shutdownOutput会关闭输出流,但是连接还是建立着的,相当于提示服务器我客户端输出完毕了。
//这样服务器才能在read方法里读出-1来,退出循环,不加shutdown服务器会在read方法处阻塞
// socket.shutdownOutput();
//读数据
InputStream is = socket.getInputStream();
byte[] readbuf = new byte[1024];
StringBuilder sb = new StringBuilder();
int len = is.read(readbuf);
while(len!=-1)
{
sb.append(new String(readbuf,0,len));
len = is.read(readbuf);
}
System.out.println("message from server:"+sb.toString());
Thread.sleep(5000);
}
}
这边客户端的请求末尾一定带上\r\n,表示发送完毕了。。不然服务器会一直等着,以为你没结束。。
我本地随便发的消息服务器都能收到,我以为我终于可以了,能拿到数据了,这烦人的东西终于结束了。。。结果,拿不到客户传的数据,白瞎。。。
我真的讨厌乱七八糟的一堆的群,真恶心。。。
补充:
因为客户消息是接收到就转发,而且第一次请求就卡在accept那里,所以合理怀疑是因为消息末尾没加\r\n导致的,其实如果他加上我应该就可以了,但是我决定先我自己解决。。。还好我比较幸运,网上有一种写法是异步接收消息,刚好能用。。
package com.cn.test;
import java.io.FileWriter;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Server {
public static final int port = 8808;
// public static final String host = "139.198.XXX.XXX";
public static void main(String[] args) {
System.out.println("Server...\n");
Server server = new Server();
server.init();
}
public void init() {
try {
//创建一个ServerSocket,这里可以指定连接请求的队列长度
ServerSocket server = new ServerSocket(port);
while (true) {
//从请求队列中取出一个连接
Socket client = server.accept();
System.out.println("服务器接收到了请求");
// 处理这次连接
waitForData(client);
// new HandlerThread(client);
}
} catch (Exception e) {
System.out.println("服务器异常: " + e.getMessage());
e.printStackTrace();
}
}
private void waitForData(Socket client) {
FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
String charset = "GBK";
byte[] buffer = new byte[10240];
String msg;
int len;
InputStream inputStream = client.getInputStream();
// BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, charset));
while (true) {
if (!client.isInputShutdown()) {
len = inputStream.read(buffer);
if (len == -1)
break;
msg = new String(buffer, 0, len, charset);
System.out.println("========数据长度: " + msg.length());
saveData(msg+"\r\n");
// dealWithData(msg.trim());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
new Thread(futureTask).start();
}
private void saveData(String data) throws Exception{
if(data == null || "".equals(data.trim())) {
return;
}
String fileUrl = "C://Users/Administrator/Desktop/electric/";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH");
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String now = sdf.format(new Date());
fileUrl = fileUrl + now + "_electric.txt";
FileWriter fw = null;
try {
fw = new FileWriter(fileUrl, true);
if(data.trim().matches("\\{.*\\}\\{.*\\}.*")) {
//{"ygdd":230322.671875,"deviceid":11140000000012,"timestamp":1670999240602}{"ygdd":230322.6796875,"deviceid":11140000000012,"timestamp":1670999244227}
data = data.trim().replace("}", "}\r\n").trim();
}
fw.write(data);
fw.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
fw = null;
}
}
}
终于拿到了他们的数据。。。。mdzz,这周脏话已经严重超标了。。。