简单学学Java网络编程——Socket
1. 前言
Java 基础 之 网络编程。
学习Socket的必要性
- 实现网络通信:Socket编程是实现网络通信的基础。通过Socket,可以在不同的计算机之间建立连接,实现数据的传输和通信。
- 客户端-服务器模型:Socket编程是构建客户端-服务器模型的关键。可以使用Socket在客户端和服务器之间建立连接,并进行双向通信,实现数据的发送和接收。
- 跨平台性:Java Socket编程是跨平台的,这意味着我们可以在不同的操作系统上使用相同的代码进行网络通信。这使得Socket编程在构建分布式系统和跨网络通信时非常有用。
- 网络安全:了解Socket编程可以帮助我们理解网络安全的基本原理和机制。
学习目标
简单学习基本的 Java Socket 使用方式。为日后深入理解Tomcat等服务器原理打下基础。
对于我们一线基层开发而言,工作中直接用到Socket的机会很少,因此本文可用当作拓展小知识阅读。
学习基础
科班必备: 408 之一 《计算机网络》
参考教程
how2j的Java教程:https://how2j.cn/
注:本文大部分代码搬运自参考教程,请读者按需食用。
2.Java Socket
2.1 获取 ip
获取本机IP
public static void main(String[] args) throws UnknownHostException {
// get local socket ip
InetAddress inetAddress = InetAddress.getLocalHost();
String ip = inetAddress.getHostAddress();
System.out.println("local ip: " + ip);
}
执行 ping 命令
public static void main(String[] args) throws IOException {
Process p = Runtime.getRuntime().exec("ping 127.0.0.1");
// using buff reader to read the result
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
if (line.length() > 0) {
sb.append(line);
}
}
System.out.println("ping finished");
System.out.println(sb);
}
若是在 Windows OS 本地执行 这个代码,有可能会因为编码问题初学乱码,但没有关系,结果中出现 TTS 就说明本次ping通了。
根据上述的例子,我们已经学习了如何通过Java代码获取本地 ip 以及执行 ping 操作。接下来我们做一个练习:
判断当前网段有多少能ping通的ip地址:
public static void main(String[] args) throws IOException, InterruptedException {
InetAddress host = InetAddress.getLocalHost();
String ip = host.getHostAddress();
String ipRange = ip.substring(0, ip.lastIndexOf("."));
System.out.println("local ip: " + ip);
System.out.println("ip range: " + ipRange);
List<String> ips = Collections.synchronizedList(new ArrayList<>());
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
10,
15,
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
AtomicInteger number = new AtomicInteger();
for (int i = 1; i < 255; i++) {
String testIp = ipRange + "." + i;
threadPool.execute(() -> {
boolean reachable = isReachable(testIp);
if (reachable) {
ips.add(testIp);
}
synchronized (number){
System.out.println("check ip: " + testIp + ", reachable: " + reachable + ", current: " + number.incrementAndGet());
}
});
}
threadPool.shutdown();
if(threadPool.awaitTermination(1, TimeUnit.HOURS)){
for (String s : ips) {
System.out.println("find access ip: " + s);
System.out.println(s);
}
System.out.println("finished, find access ips: " + ips.size());
}
}
/**
* find access ips
*/
private static boolean isReachable(String ip){
String success = "TTL";
try {
boolean reachable = false;
Process p = Runtime.getRuntime().exec("ping -n 1 " + ip);
BufferedReader br = new BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
if (line.length() > 0) {
sb.append(line+"\r\n");
}
}
// if contains TTL then reachable
if(sb.toString().contains(success)){
reachable = true;
}
br.close();
return reachable;
}catch (IOException e){
e.printStackTrace();
return false;
}
}
2.2 Socket 套接字
本文参考的教程直接用Socket简单实现了一个Server和Client,通过写这两个简单的Servier 和 Client, 我们可以了解到Socket的基本用法:
Server
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
System.out.println("server started, waiting for client...");
Socket s = ss.accept();
s.close();
ss.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Client
public class Client {
public static void main(String[] args) {
try {
Socket s = new Socket("127.0.0.1",8888);
System.out.println(s);
s.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Socket接发消息
Client :
public static void main(String[] args) {
try {
Socket s = new Socket("127.0.0.1",8888);
System.out.println(s);
OutputStream os = s.getOutputStream();
os.write(97);
os.close();
s.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Server :
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
System.out.println("server started, waiting for client...");
Socket s = ss.accept();
InputStream is = s.getInputStream();
int msg = is.read();
System.out.println("msg from client: " + msg);
is.close();
s.close();
ss.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
3. 练习
如果是为了应付课内考试的同学,可参考以下代码进行练习。
双工Demo
基于上面章节列举的Server、Client demo,实现双工demo:
ublic class DoubleClient {
public static void main(String[] args) {
try {
Socket s = new Socket("127.0.0.1", 8888);
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
Scanner sc = new Scanner(System.in);
System.out.print("client says: ");
String str = sc.next();
dos.writeUTF(str);
if("bye".equals(str)) {
break;
}
String msg = dis.readUTF();
System.out.println("server says: " + msg);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class DoubleServer {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
System.out.println("server is running...");
Socket s = ss.accept();
System.out.println("client is connected...");
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
while (true){
String msg = dis.readUTF();
System.out.println("client says: " + msg);
if("bye".equals(msg)) {
break;
}
Scanner sc = new Scanner(System.in);
System.out.print("server says: ");
String str = sc.next();
dos.writeUTF(str);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
多线程聊天
结合多线程,做一个小练习
发送线程:
public class SendThread extends Thread {
private Socket s;
public SendThread(Socket s) {
this.s = s;
}
public void run(){
try {
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
while(true){
Scanner sc = new Scanner(System.in);
String str = sc.next();
dos.writeUTF(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收线程:
public class AcceptThread extends Thread {
private Socket s;
public AcceptThread(Socket s) {
this.s = s;
}
public void run() {
try {
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true) {
String msg = dis.readUTF();
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端:
public class ThreadServer {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
System.out.println("监听在端口号:8888");
Socket s = ss.accept();
//启动发送消息线程
new SendThread(s).start();
//启动接受消息线程
new AcceptThread(s).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
public class ThreadClient {
public static void main(String[] args) {
try {
Socket s = new Socket("127.0.0.1", 8888);
// 启动发送消息线程
new SendThread(s).start();
// 启动接受消息线程
new AcceptThread(s).start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 总结
本文介绍了 Socket 套接字的基本使用,实际工作中用得少,但是也是Java学习的必经之路。本文记录了一些节选自参考教程的练习,读者可按需选用。