java使用socket连接php-fpm,使用FastCGI协议直接通讯
FastCGI交互时序,从上到下,FCGI_BEGIN_REQUEST开始:
FastCGI协议:
- 基于cgi,添加守护进程管理cgi程序,这样就不用像原始cgi那样,每个请求来fork新建cgi进程处理,处理完销毁进程的开销。
- FastCGI如上交互时序,然后每条报文都有8byte报文头标识报文类型与长度
- FCGI_BEGIN_REQUEST,FCGI_END_REQUEST是固定长度,确定请求的开始于结束
- 其他params,stdin,stdout等等都是不定长,每次发最大2byte 65536长度,由报文头的contentLength为2byte决定,可以连续多发,【必须】最后跟一个【同类型】contentLength为0的空报文标识这个类型的消息发送完毕
下面的代码可以远程ip调用,php-fpm配置改为监听0.0.0.0::9000
请求的param报文中确定访问的php文件与路径,确保php有权限,不行就试试777
import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) throws Exception{
Socket client=new Socket("127.0.0.1",9000);
InputStream in=client.getInputStream();
OutputStream out=client.getOutputStream();
// protocol sequence
int request_id=5678;
//1.begin request
byte[] begin_request_body = new byte[8];
begin_request_body[0]=0; // roleB1
begin_request_body[1]= fcgi_role.FCGI_RESPONDER; //roleB0
begin_request_body[2]=0; // flags
byte[] begin_request = fcgi.fcgiPacket(fcgi_request_type.FCGI_BEGIN_REQUEST,request_id,begin_request_body);
System.out.println("FCGI_BEGIN_REQUEST:\n"+ Base64.getEncoder().encodeToString(begin_request));
//2.params
Map<String,String> params = new LinkedHashMap<>();
params.put("GATEWAY_INTERFACE","FastCGI/1.0");
params.put("REQUEST_METHOD","GET");
params.put("SCRIPT_FILENAME","/home/qianggetaba/test.php");
params.put("SCRIPT_NAME","/test.php");
params.put("QUERY_STRING","");
params.put("REQUEST_URI","/test.php");
params.put("DOCUMENT_URI","/test.php");
params.put("SERVER_SOFTWARE","php/fcgiclient");
params.put("REMOTE_ADDR","127.0.0.1");
params.put("REMOTE_PORT","9985");
params.put("SERVER_ADDR","127.0.0.1");
params.put("SERVER_PORT","80");
params.put("SERVER_NAME","DESKTOP-NCL22GF");
params.put("SERVER_PROTOCOL","HTTP/1.1");
params.put("CONTENT_TYPE","");
params.put("CONTENT_LENGTH","0");
System.out.println("\nparam pair base64:");
ByteBuffer paramContainer = ByteBuffer.allocate(1024);
for(String key:params.keySet()){
byte[] onePair = fcgi.fcgiParam(key,params.get(key));
paramContainer.put(onePair);
System.out.println(key+" - "+params.get(key) +": "+Base64.getEncoder().encodeToString(onePair));
}
paramContainer.flip();
byte[] fcgi_param_byte = new byte[paramContainer.remaining()];
paramContainer.get(fcgi_param_byte);
byte[] fcgi_params = fcgi.fcgiPacket(fcgi_request_type.FCGI_PARAMS,request_id,fcgi_param_byte);
System.out.println("\nFCGI_PARAMS:\n"+Base64.getEncoder().encodeToString(fcgi_params));
byte[] fcgi_params_end = fcgi.fcgiPacket(fcgi_request_type.FCGI_PARAMS,request_id,new byte[0]);
System.out.println("\nFCGI_PARAMS end with empty content:\n"+Base64.getEncoder().encodeToString(fcgi_params_end));
//3.stdin
byte[] fcgi_stdin = fcgi.fcgiPacket(fcgi_request_type.FCGI_STDIN,request_id,new byte[0]);
System.out.println("\nFCGI_STDIN:\n"+Base64.getEncoder().encodeToString(fcgi_stdin));
ByteBuffer onePacket = ByteBuffer.allocate(begin_request.length+fcgi_params.length+fcgi_params_end.length+fcgi_stdin.length);
onePacket.put(begin_request);
onePacket.put(fcgi_params);
onePacket.put(fcgi_params_end);
onePacket.put(fcgi_stdin);
onePacket.flip();
System.out.println("\nall:\n" +Base64.getEncoder().encodeToString(onePacket.array()));
out.write(onePacket.array());
System.out.println("\noutput:");
byte[] buf = new byte[1024];
int len;
while ((len=in.read(buf))!=-1){
// decode byte[] packet by yourself
System.out.println(new String(buf,0,len,"UTF-8"));
}
}
}
class fcgi {
static byte VERSION = 1;
public static byte[] fcgiPacket(byte type, int id, byte[] content) {
//header
byte[] header = new byte[8];
header[0] = fcgi.VERSION;
header[1] = type;
header[2] = (byte) ((id >> 8) & 0xff);//requestIdB1
header[3] = (byte) (id & 0xff); // requestIdB0, big endian
header[4] = (byte) ((content.length >> 8) & 0xff);//contentLengthB1
header[5] = (byte) (content.length & 0xff); //contentLengthB0
header[6] = 0; //padding length
header[7] = 0; //reserved
//combine header and content to one byte[]
byte[] packet = new byte[header.length + content.length];
System.arraycopy(header, 0, packet, 0, header.length);
System.arraycopy(content, 0, packet, header.length, content.length);
return packet;
}
public static byte[] fcgiParam(String name, String value) {
int nameLength = name.length();
int valueLength = value.length();
byte[] nameLen, valueLen;
if (nameLength < 128) {
nameLen = new byte[1];
nameLen[0] = (byte) nameLength;
} else {
nameLen = new byte[4];
nameLen[0] = (byte) ((nameLength >> 24) & 0xff);
nameLen[1] = (byte) ((nameLength >> 16) & 0xff);
nameLen[2] = (byte) ((nameLength >> 8) & 0xff);
nameLen[3] = (byte) (nameLength & 0xff);
}
if (valueLength < 128) {
valueLen = new byte[1];
valueLen[0] = (byte) valueLength;
} else {
valueLen = new byte[4];
valueLen[0] = (byte) ((valueLength >> 24) & 0xff);
valueLen[1] = (byte) ((valueLength >> 16) & 0xff);
valueLen[2] = (byte) ((valueLength >> 8) & 0xff);
valueLen[3] = (byte) (valueLength & 0xff);
}
byte[] onePair=new byte[nameLen.length+valueLen.length+name.getBytes().length+value.getBytes().length];
System.arraycopy(nameLen,0,
onePair,0,nameLen.length);
System.arraycopy(valueLen,0,
onePair,nameLen.length,valueLen.length);
System.arraycopy(name.getBytes(),0,
onePair,nameLen.length+valueLen.length,name.getBytes().length);
System.arraycopy(value.getBytes(),0,
onePair,nameLen.length+valueLen.length+name.getBytes().length,value.getBytes().length);
return onePair;
}
}
class fcgi_request_type {
static byte FCGI_BEGIN_REQUEST = 1;
static byte FCGI_ABORT_REQUEST = 2;
static byte FCGI_END_REQUEST = 3;
static byte FCGI_PARAMS = 4;
static byte FCGI_STDIN = 5;
static byte FCGI_STDOUT = 6;
static byte FCGI_STDERR = 7;
static byte FCGI_DATA = 8;
static byte FCGI_GET_VALUES = 9;
static byte FCGI_GET_VALUES_RESULT = 10;
}
class fcgi_role{
static byte FCGI_RESPONDER = 1;
static byte FCGI_AUTHORIZER = 2;
static byte FCGI_FILTER = 3;
}