网络编程
1.1、概述
现在的世界,可以实现你在家里(海口)发送一个消息,很短的时间内,在印度的阿老师们就能够收到。这就是一个常见的网络使用的例子。
地球村的概念提出,更加完美的诠释了网络的效应。
在上世纪,最常用的通讯方式,还是信件。以下是信件的简式结构:
计算机网络:
计算机网络是指将**地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来**,在网络操作系统,网络管理软件及**网络通信协议**的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程的目的:
类比于无线电台,用于进行传播交流信息。进行数据交换,通信。
需要达到的效果,成果是什么:
1.如何准确定位网络上的一台主机?
- 通过端口号来进行。Eg:192.167.7.7
2.找到了主机,如何进行数据传递?
- ……
1.2、网络通信要素
随便举个例子,人工智能中的智能汽车。
目前适用于工程,码头等人少的地方,少数地方在尝试上路。由于伦理道德问题,难以大规模上路。网络通信的内容是很复杂,繁多的。
怎样实现网络通信?
通信双方的地址:
-
IP
-
端口
-
局域网地址等……
规则?网络通信协议
以TCP/IP协议为例:
我们重点学习传输层(TCP,UDP协议),以其为主题,进行讲解。
小结:
1.网络编程中有两个主要问题:
- 如何准确的定位到网络上的一台或多台主机。
打开CMD,直接输入“ping + 目标网址”进行地址跳跃查询。
- 找到主机后如何进行通信。
2.网络编程中的要素:
-
IP和端口号。
-
网络通信协议。
3.Java中万物皆对象,使用何种对象进行信息传输?
1.3、IP地址
Java中如何进行IP地址的获取?
使用“InetAddress”关键词。
-
IP的作用:唯一定位一台网络上的计算机。
-
特殊IP地址的记忆:127.0.0.1:本机地址,又称为localhost。
-
IP地址的分类:
一、IPV4/IPV6
1.IPV4:127.0.0.1,由4个字节居中,每个字节的大小为0到255,约有42亿个地址。其中约有30亿在北美,4亿在亚洲。在2011年地址已用尽。
2.IPV6:fe70::5477:2017:d5ac:7958%7,由128位,8个无符号整数组成。具有更多的地址数,可满足地球上每一粒沙子分配一个地址。
二、公网(互联网)/私网(局域网)
1.ABCD类地址:
2.192.168.XX.XX,等,专供某些组织使用。
- 域名:方便记忆的IP地址问题。
在阿里云上可以搜查你感兴趣的IP地址的价格。
代码测试:
package com.edwin.lesson1;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author EdwinD
* @create 2020.08.20 下午 01:24
* @desc 测试Ip地址。
**/
public class TextInetAddress {
public static void main(String[] args) {
try {
// 查询本机地址
// 方法一:
InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress1);
// 方法二:
InetAddress inetAddress2 = InetAddress.getByName("localhost");
System.out.println(inetAddress2);
// 方法三:
InetAddress inetAddress3 = InetAddress.getLocalHost();
System.out.println(inetAddress3);
// 查询百度的地址
InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress4);
// 常用的方法
System.out.println(inetAddress4.getAddress());
System.out.println(inetAddress4.getCanonicalHostName());//规范的名字
System.out.println(inetAddress4.getHostAddress());//IP
System.out.println(inetAddress4.getHostName());//域名,或者自己电脑的名字
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
输出效果:
1.4、端口
端口表示计算机上的一个程序的进程。
IP和端口号好比一个大楼和里面一个个房间。
-
不同的进程有不同的端口号。用来区分软件。
-
范围规定:0—65535。
-
分为TCP和UDP两个,都是65535个。两个协议下,端口号不能够重复。
-
端口分类:
1.共有端口:0—1023
-
HTTP:80
-
HTTPS:443
-
FTP:21
-
Telent:23
2.程序注册端口:1024—49151,用于分配给用于或者程序。
-
Tomcat:8080
-
Mysql:3306
-
Oracle:1521
3.动态、私有:49152—65535
netstat -ano;#查看所有的端口
netstat -ano|findstr "XXXX";#查看指定的XXXX端口号
tasklist|findstr "XXXX";#查找某个端口的进程
代码:
package com.edwin.lesson1;
import java.net.InetSocketAddress;
/**
* @author EdwinD
* @create 2020.08.20 下午 03:11
* @desc 套接字
**/
public class TestInetSocketAddress {
public static void main(String[] args) {
InetSocketAddress socketAddress1 = new InetSocketAddress("127.0.0.1", 8080);
System.out.println(socketAddress1);
InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080);
System.out.println(socketAddress2);
System.out.println(socketAddress1.getAddress());
System.out.println(socketAddress1.getHostName());
System.out.println(socketAddress1.getPort() );
}
}
输出:
运行原理:
1.5、通信协议
所谓协议,就是一些约定,用于大家一起更好地使用一些东西。就比如中国人的官方语言是普通话。
**网络通信协议:**针对网络的一些规定。一些特殊单位:速率,传输码率,代码结构,传输控制等……
但是这些协议错综复杂,十分难顶。
因此,有了一个“大事化小”的方法:分层。
这其中,最重要的协议是:TCP/IP协议簇
其中重要的:
-
TCP:用户传输协议
-
UDP:用户数据报协议
其中出名的协议:
-
TCP:
-
IP:网络连接协议
TCP和UDP对比
1.TCP:
-
建立连接,稳定。
-
“三次握手”连接和“四次挥手”断开。
//三次握手:至少进行3次,才能保障连接。
A:你愁啥?
B:瞅你咋地?
A:干你丫的!
三次握手:
//四次挥手:至少进行4此,才能保障断开。
A:我要走了。
B:你真的要走了吗?
B:你真的真的要走了吗?
A:我真的真的要走了。
四次挥手:
- 客户端、服务端界限明朗。
2.UDP:
-
不连接,不稳定。
-
客户端、服务端的界限不明朗。
-
不论对方是否准备好,都可以给对方发消息。
-
DDOS:洪水攻击,一次性发送大量的数据,进行饱和攻击。
1.6、TCP
传输中添加通道的原理:
从一端来的输入,可能无法被另外一边的理解,添加了这个通道,就相当于进行了一次翻译,让两边都能理解。
1、文字传输
客户端
1.连接服务器—>Socket
2.发送消息
3.代码:
package com.edwin.lesson2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author EdwinD
* @create 2020.08.20 下午 05:07
* @desc 客户端1
**/
public class TcpClient1 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
// 1.需要知道服务器的地址
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
// 2.设置连接端口号
int port = 7777;
// 3.创建一个socket,进行连接。Socket是有参数的,默认无参数
socket = new Socket(serverIP, port);
// 4.发送消息:IO流
os = socket.getOutputStream();
os.write("Welcome,EdwinD is Learning Web".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器
1.建立服务的端口—>ServerSocket
2.等待用户的连接—>Accept
3.代码:
package com.edwin.lesson2;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author EdwinD
* @create 2020.08.20 下午 05:08
* @desc 服务端1
**/
public class TcpServer1 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
// 提供服务器的地址:serverSocket,套接字。
// 1.我有一个地址
serverSocket = new ServerSocket(7777);
while (true) {
// 2.等待客户链接过来,这里的socket等同于TcpClient1里面的socket,
socket = serverSocket.accept();
// 3.读取客户端
is = socket.getInputStream();
/*
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer))! = -1){
String msg = new String(buffer,0,len);
System.out.println(msg);
}
*/
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
}
} catch (IOException e) {
e.printStackTrace();
}finally {
// 关闭资源
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
2、文件上传
同样分为客户端和服务端。
客户:
package com.edwin.lesson2;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
/**
* @author EdwinD
* @create 2020.08.20 下午 06:06
* @desc 客户端2
**/
public class TcpClient2 {
public static void main(String[] args) throws IOException {
// 1.创建一个Socket连接
// 注意port不可以和Client1的重合,否则会报错。
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 7788);
// 2.创建一个输出流
OutputStream os = socket.getOutputStream();
// 3.文件流
FileInputStream fils = new FileInputStream(new File("Study.jpg"));
// 4.写出文件
byte[] buffer = new byte[1024];
int len;
while ((len = fils.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 通知服务器,已经结束了
socket.shutdownInput();
// 确定接受完毕,才能断开连接
InputStream inputStream = socket.getInputStream();
// 接受的是String类型的数组,
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while ((len2 = inputStream.read(buffer2)) != -1) {
baos.write(buffer2, 0, len2);
}
System.out.println(baos.toString());
// 5.关闭资源
baos.close();
inputStream.close();
fils.close();
os.close();
socket.close();
}
}
服务:
package com.edwin.lesson2;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author EdwinD
* @create 2020.08.20 下午 06:14
* @desc 服务端2
**/
public class TcpServer2 {
public static void main(String[] args) throws IOException {
// 1.创建服务
ServerSocket serverSocket = new ServerSocket(7788);
// 2.监听客户端的连接
Socket socket = serverSocket.accept();//阻塞式监听,会一直等待客户端连接,一旦链接,就失败了。
// 3.得到输入流
InputStream is = socket.getInputStream();
// 4.文件输出
FileOutputStream receive = new FileOutputStream(new File("receive.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
receive.write(buffer, 0, len);
}
// 通知客户端,接收完成
OutputStream os = socket.getOutputStream();
os.write("接受完毕,可以断开。".getBytes());
// 5.关闭资源
receive.close();
is.close();
socket.close();
serverSocket.close();
}
}
输出:
1.7、Tomcat
上两个例子,我们的客户端和服务端,均由自定义写出来的。
服务器:
-
自定义
-
使用Tomcat
客户端:
-
自定义
-
浏览器
1.8、UDP
类比发短信,不需要进行连接,但是需要知道对方的地址。
1.发送消息
发送端:
package com.edwin.lesson3;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.20 下午 08:48
* @desc 客户端1,不需要连接服务器
**/
public class UdpClient1 {
public static void main(String[] args) throws Exception {
// 1.建立一个Socket
DatagramSocket socket = new DatagramSocket();
// 2.建一个包
String msg = "Hello,服务器。";
// 发送给谁
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
// 参数内容:数据,数据的起始,数据的长度,数据要发送给谁。
DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
// 3.发送包
socket.send(packet);
// 4.关闭流
socket.close();
}
}
接收端:
package com.edwin.lesson3;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.21 下午 09:19
* @desc Udp服务端1
**/
public class UdpServer1 {
public static void main(String[] args) throws IOException {
// 开放端口
DatagramSocket socket = new DatagramSocket(9090);
// 接受数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
// 阻塞接受
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(),0, packet.getLength()));
// 关闭连接
socket.close();
}
}
输出效果:
2.单方向聊天
一方可以不停给对方发送消息。
代码块儿
发送方:
package com.edwin.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.23 上午 10:28
* @desc 发送2.0
**/
public class UdpSender2 {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8868);
// 准备数据:读取控制台写入:System.in
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6686));
socket.send(packet);
if (data.equals("bye")) {
break;
}
}
socket.close();
}
}
接收方:
package com.edwin.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author EdwinD
* @create 2020.08.23 上午 10:29
* @desc 接收2.0
**/
public class UdpReceive2 {
public static void main(String[] args) throws IOException {
DatagramSocket sock = new DatagramSocket(6686);
while (true) {
// 准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
// 阻塞式接受包裹
sock.receive(packet);
// 断开连接—>bye
byte[] data = packet.getData();
String receiveData = new String(data, 0, data.length);
System.out.println(receiveData);
if (receiveData.equals("bye")) {
break;
}
}
sock.close();
}
}
效果输出:
后期改进后,连两方均可是发送方,也可以是接收方。
3.双方互通聊天
双方聊天需要保障,两个人发出的消息均可被对方接受。
体系结构:
代码区:
TalkSend:
package com.edwin.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.23 上午 10:54
* @desc 聊天发送
**/
public class TalkSend implements Runnable {
DatagramSocket socket = null;
BufferedReader reader = null;
private int fromIp;
private String toIp;
private int toPort;
public TalkSend(int fromIp, String toIp, int toPort) throws SocketException {
this.fromIp = fromIp;
this.toIp = toIp;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromIp);
// 准备数据:读取控制台写入:System.in
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
String data = null;
try {
data = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIp, this.toPort));
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
if (data.equals("bye")) {
break;
}
}
socket.close();
}
}
TalkReceive:
package com.edwin.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.23 上午 11:19
* @desc 聊天接收
**/
public class TalkReceive implements Runnable{
DatagramSocket sock = null;
private int port;
private String msgFrom;
public TalkReceive(int port,String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
sock = new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
try {
// 准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
// 阻塞式接受包裹
sock.receive(packet);
// 断开连接—>bye
byte[] data = packet.getData();
String receiveData = new String(data, 0, data.length);
System.out.println(msgFrom+":"+receiveData);
if (receiveData.equals("bye")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
sock.close();
}
}
TalkStudent:
package com.edwin.chat;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.23 上午 11:25
* @desc 学生端,双线程聊天。
**/
public class TalkStudent {
public static void main(String[] args) throws SocketException {
// 开启两个线程
new Thread(new TalkSend(7777,"localhost",9999)).start();
new Thread(new TalkReceive(8888,"老师")).start();
}
}
TalkTeacher:
package com.edwin.chat;
import java.net.SocketException;
/**
* @author EdwinD
* @create 2020.08.23 上午 11:26
* @desc 老师端,双线程聊天。
**/
public class TalkTeacher {
public static void main(String[] args) throws SocketException {
// 开启两个线程
new Thread(new TalkSend(5555,"localhost",8888)).start();
new Thread(new TalkReceive(9999,"学生")).start();
}
}
效果输出:
IDEA中:
CMD中:
在使用了IDEA中的交互聊天后,我们考虑能否分窗口进行聊天,我们找到目录下class文件的所在地。
在目录下打开CMD,进行class文件的操作。
在路径前输入CMD加空格后回车,即可。
启动TalkStudent,但是无法启动。
因为,这里需要将:com.edwin.chat一同加在TalkStudent之前,他们是一个有机的整体。
启动IDEA中的TalkTeacher后,进行互动,效果如下:
由于CMD的格式原因,导致双方中文乱码,更改IDEA中的输出格式即可。
1.9、URL
URL:统一资源定位系统(uniform resource locator;URL)是因特网的万维网服务程序上用于指定信息位置的表示方法。它最初是由[蒂姆- 伯纳斯- 李](https://baike.baidu.com/item/蒂姆- 伯纳斯- 李)发明用来作为万维网的地址。
用于进行定位互联网上的某一特定资源。
DNS:域名解析。用于将某一个IP地址用WWW.XXXX.com表示出来。
# URL的本质是一个协议
# 表达的内容,是IP地址和端口号,项目名和资源等。
1.URL相关的参数
测试形代码:
package com.edwin.lesson4;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @author EdwinD
* @create 2020.08.23 下午 12:35
* @desc URL
**/
public class URL1 {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://loaclhost:8080/helloworld/index.jsp?username=edwin&password=1234");
System.out.println(url.getProtocol());//获得协议名
System.out.println(url.getHost());//主机ip
System.out.println(url.getPort());//端口
System.out.println(url.getPath());//全路径
System.out.println(url.getFile());//文件
System.out.println(url.getQuery());//参数
}
}
输出效果:
相关的参数不一定真实。
2.网络抓包,文件下载
可以通过某个确定的Url,进行网络特定资源的下载。我们可以先从本地的文件开始:
(1)本地文件
在本地的Tomcat里创建一个web页面:
内容:
打开本地的Tomcat文件夹里面bin目录里面的“startup.bat”:
访问Tomcat的http://localhost:8080/Edwin_Web_Try/Secret.txt:
代码:
package com.edwin.lesson4;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* @author EdwinD
* @create 2020.08.23 下午 09:52
* @desc Url下载文件
**/
public class UrlDownload {
public static void main(String[] args) throws IOException {
// 建立Url连接,设置下载地址.
URL url = new URL("http://localhost:8080/Edwin_Web_Try/Secret.txt");
// 连接到这个资源的Url,http
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("Secret2.txt");
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}
输出效果:
(2)网络资源下载
1.图片类:
转换成网络连接:
https://th.wallhaven.cc/small/4y/4y2mo7.jpg
图片效果:
双击打开图片:
代码:
package com.edwin.lesson4;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* @author EdwinD
* @create 2020.08.23 下午 09:52
* @desc Url下载文件
**/
public class UrlDownload {
public static void main(String[] args) throws IOException {
// 建立Url连接,设置下载地址.
URL url = new URL("https://p1.music.126.net/KDZoWvE6pjfeZ56bVD6giw==/109951164009637404.png");
// 连接到这个资源的Url,http
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("Vip.png");
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}
输出效果:
2.歌曲类:
安全版:
通过ctrl+shift+c或者直接打开F12,打开网络后台控制页。
双击打开:
将链接复制到我们下载文件的地方,Url。
代码:
package com.edwin.lesson4;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* @author EdwinD
* @create 2020.08.23 下午 09:52
* @desc Url下载文件
**/
public class UrlDownload {
public static void main(String[] args) throws IOException {
// 建立Url连接,设置下载地址.
URL url = new URL("https://m701.music.126.net/20200824091233/d716ecaacea305a909aa539f0da1658d/jdyyaac/045a/0253/065e/303e34d46a59c98f75c41471b3b57a4c.m4a");
// 连接到这个资源的Url,http
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
//下载文件命名。
FileOutputStream fos = new FileOutputStream("J-fla.m4a");
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
//关闭资源
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}
输出效果:
路漫漫其修远兮,吾将上下而求索。
参考文献
《【狂神说Java】网络编程实战讲解》
视频链接
2020.08.24