第十一章、Java基础语法----网络编程
本人也是刚入门Java语言,可能会有一些地方出现错误,描述的不对。如果发现不对的地方请及时指出,好对其进行修改。这样不仅可以让我学到东西,也可以让其他刚入门的朋友学习更正确的内容。
所有内容仅供参考。不具有完全的准确性!
注:关于Java的所有内容都会参考到尚硅谷在网上公开的学习视频及其提供的PPT
推荐:
https://www.cnblogs.com/swordfall/p/10781281.html
https://blog.csdn.net/qq_34508384/article/details/87598138
https://blog.csdn.net/weixin_43907332/article/details/86583833
一、网络编程的概述
Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
(一)网络是什么
计算机网络就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
(二)网络编程的目的:
直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
(三)网络编程中有两个主要的问题
- 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
- 找到主机后如何可靠高效地进行数据传输
二、网络通信要素概述
(一)如何实现网络中的主机互相通信?
- 获取通信双方地址
① IP (确定具体的主机)
② 端口号 (确定对应主机上指定的应用程序) - 规则:(即:网络通信协议)
① OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
② TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
(二)网络通信协议
目前的使用最广泛的就是TCP/IP模型,下边也主要以TCP/IP的模型讲述
三、通信要素一:IP 和 端口号
(一)IP的理解
- IP:唯一的标识 Internet 上的计算机(通信实体)
- 在Java中使用InetAddress类代表IP
- IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
- 域名: www.baidu.com www.mi.com www.sina.com www.jd.com
域名解析:使用域名要比IP地址更容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析 - 本地回路地址:127.0.0.1 对应着:localhost
(二)端口号:正在计算机上运行的进程。
- 要求:不同的进程不同的端口号
- 范围:被规定为一个 16 位的整数 0~65535。
端口号与IP地址的组合得出一个网络套接字:Socket
(三)InetAddress类:此类的一个对象就代表着一个具体的IP地址
- 实例化的方式
InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取实例
getByName(String host) 、 getLocalHost() - 常用方法
getHostName() / getHostAddress()
public void test1(){
try {
// 1.实例化
InetAddress byName = InetAddress.getByName("192.168.0.1");
System.out.println(byName);///192.168.0.1
//获取本地ip
InetAddress host = InetAddress.getLocalHost();
System.out.println(host);//DESKTOP-5ABHKJN/10.204.155.94
// 127.0.0.1是回送地址,指本地机,用来测试使用
InetAddress byName2 = InetAddress.getByName("127.0.0.1");
System.out.println(byName2);///127.0.0.1
InetAddress byName1 = InetAddress.getByName("www.baidu.com");
System.out.println(byName1);//www.baidu.com/110.242.68.3
//getHostName()
System.out.println(byName1.getHostName());//www.baidu.com
//getHostAddress()
System.out.println(byName1.getHostAddress());//110.242.68.3
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
关于LocalHost,本地IP和127.0.0.1三者的区别:
https://www.cnblogs.com/psy-sdudio/p/11530592.html
四、 通信要素二:网络协议
(一)网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
(二)TCP/IP协议簇
传输层协议中有两个非常重要的协议:
- 传输控制协议TCP
- 用户数据报协议UDP
(三)TCP和UDP的区别
(四)TCP的三次握手
通俗来说:
第一次握手(客户端):喂,服务器,你能听到么
第二次握手(服务器):我可以听到,你能听到我的么
第三次握手(客户端):我也可以,现在我们可以通信啦
为什么不是一次、两次或三次以上?
假设一次握手:此时服务器可能没有收到客户端的连接请求,在这种情况下传输数据是不稳定的。
假设两次握手:在服务器返回给客户端的时候,此时客户端可能突然发生异常,断开了连接。这种情况下服务器向客户端发送信息就会发生不稳定的情况。
假设四次及以上握手:此时客户端和服务器相互发送信息是没有任何问题的,而且要比三次更稳定。为什么不适用四次及以上的原因是因为三次的连接成功率已经满足正常使用的需求了,假设三次的成功率是百分之99,那么使用四次及以上只会提高一丝的成功率,且还需要多进行一次握手。相比还是三次握手的效率要更好。
(五)TCP的四次握手
刚才不是说了不使用四次握手吗,现在怎么又有四次握手了?
TCP中的四次握手是释放连接的握手,三次是建立连接的握手
通俗来说:
第一次(客户端):喂,我说完了,我下了啊
第二次(服务器):等等,我还有事
第三次(服务器):好了,我说完了
第四次(客户端):好,拜拜
(六)Socket
五、TCP的使用
注意:TCP协议要求客户端在建立连接之前需要启动服务端 ,否之会报异常
例一:客户端发送信息给服务端,服务端将数据显示在控制台上
//客户端
@Test
public void test1(){
Socket socket = null;//传参列表除了要指定对方的IP地址,还要指明对应设备的端口号(端口号指明对应的程序)
OutputStream os = null;
try {
// 1.创建Socket对象,指明服务器端的ip和端口号
InetAddress host = InetAddress.getLocalHost();
socket = new Socket(host,8190); //此处指明的端口号要和服务端的保证一致。否则在传输的过程找不到正确的发送端口
// port n.港口 在此处表端口
// 2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据的操作
os.write("hello,您好".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
...省略
}
}
//服务端
// 服务端不需要指明IP地址
@Test
public void test2(){
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
ByteArrayOutputStream bos = null;
try {
// 1.创建服务器端的ServerSocket,指明自己的端口号
serverSocket = new ServerSocket(8190);
// 2.调用accept()表示接收来自于客户端的socket
socket = serverSocket.accept();
// 3.获取输入流
inputStream = socket.getInputStream();
// 4.读取输入流中的数据
//不建议这样写,可能会有乱码
// byte[] buffer = new byte[5];
// int len;
// while((len = inputStream.read(buffer)) != -1){
// String str = new String(buffer,0,len);
// System.out.print(str);//hello,您���
// }
bos = new ByteArrayOutputStream();
byte[] cbuf = new byte[10];
int len;
while((len = inputStream.read(cbuf)) != -1){
//写进ByteArrayOutputStream容器中,然后通过toString()可以输出
bos.write(cbuf,0,len);
}
System.out.println(bos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. 关闭资源
...省略
}
}
例二:客户端向服务器发送文件,服务器将文件保存在本地
//客户端
@Test
public void test1(){
Socket socket = null;
OutputStream outputStream = null;
FileInputStream fis = null;
try {
// 1.创建Socket对象,指明服务器端的ip和端口号
// InetAddress byName = InetAddress.getByName("127.0.0.1");
// Socket socket = new Socket(byName, 9876);
socket = new Socket(InetAddress.getByName("127.0.0.1"), 9876);
// 2.获取一个输出流,用于输出数据
outputStream = socket.getOutputStream();
// 3.指明输出信息
fis = new FileInputStream("Lisa.jpg");
// 4.输出
byte[] buffer = new byte[5];
int len;
while((len = fis.read(buffer)) != -1){
outputStream.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5.关闭资源
...省略
}
}
//服务端
@Test
public void test2(){
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
FileOutputStream fos = null;
try {
// 1.创建服务器端的ServerSocket,指明自己的端口号
serverSocket = new ServerSocket(9876);
// 2.调用accept()表示接收来自于客户端的socket
socket = serverSocket.accept();
// 3..获取输入流
inputStream = socket.getInputStream();
// 4.创建输出流
//因为此处要输出的是文件,不能使用ByteArrayOutputStream,需要使用FileOutputStream
fos = new FileOutputStream(new File("Lisa5.jpg"));
// 5.操作数据
byte[] buffer = new byte[5];
int len;
while((len = inputStream.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6.关闭资源
...省略
}
}
例三:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。
//客户端
@Test
public void test1(){
Socket socket = null;
OutputStream outputStream = null;
InputStream inputStream = null;
ByteArrayOutputStream baos = null;
FileInputStream fis = null;
try {
// 1.创建Socket对象,指明服务器端的ip和端口号
InetAddress inet = InetAddress.getLocalHost();
socket = new Socket(inet, 9898);
// 2.获取一个输出流,用于输出数据
outputStream = socket.getOutputStream();
// 3.输出信息
//输出文件
//输出要使用FileInput
fis = new FileInputStream("Lisa.jpg");
byte[] buffer1 = new byte[5];
int len;
while((len = fis.read(buffer1)) != -1){
outputStream.write(buffer1,0,len);
}
//!!!当不再输出数据时,要关闭数据的输出。否则无法接受来自服务器返回的数据
socket.shutdownOutput();
// 4.接受服务器返回的数据
inputStream = socket.getInputStream();
//接收文字信息要使用ByteArrayOutputStream!!!!
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len1;
while((len1 = inputStream.read(buffer)) != -1){
baos.write(buffer,0,len1);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5.关闭资源
..省略了
}
}
//服务端
@Test
public void test2(){
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
ByteArrayOutputStream baos = null;
FileOutputStream fos = null;
try {
// 1.创建服务器端口号
serverSocket = new ServerSocket(9898);
// 2.接收客户端的数据
socket = serverSocket.accept();
// 3.创建输出流
inputStream = socket.getInputStream();
// 4.输出信息
//用于接收文件 (接收文件要有存放文件的位置)
fos = new FileOutputStream("Lisa6.jpg");
byte[] buffer = new byte[5];
int len;
while((len = inputStream.read(buffer)) != -1){
fos.write(buffer,0,len);
}
//接收文字信息要使用ByteArrayOutputStream
// 5.反馈给客服端信息
outputStream = socket.getOutputStream();
outputStream.write("已经收到了你发送的信息".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6.关闭资源
....省略了
}
六、UDP的使用
UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。类似广播,我只负责播报,至于谁听到就不关了
UDP不要先启动服务器也可以运行客户端,但发送的信息不会到达目的地。
//客户端
@Test
public void sender(){
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
String str = new String("这是我发送的一个UDP导弹,请注意查收");
byte[] data = str.getBytes();
//获取地址和指明端口
InetAddress inet = InetAddress.getLocalHost();
// 打包
DatagramPacket packet = new DatagramPacket(data, 0, data.length, inet, 9632);
// 发送
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(socket != null)
socket.close();
}
}
//服务器
@Test
public void receiver(){
DatagramSocket socket = null;
try {
// 创建端口号
socket = new DatagramSocket(9632);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
// 拆包
socket.receive(packet);
System.out.println(new String(packet.getData(),0, packet.getLength()));
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null)
socket.close();
}
}
七、URL的使用
URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一资源的地址。
(一)URL的5个基本结构:
(二)如何实例化
URL url = new URL(“http://localhost:8080/examples/beauty.jpg?username=Tom”);
(三)常用方法
实例:
常用方法
public void test1(){
try {
URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");
// public String getProtocol( ) 获取该URL的协议名
System.out.println(url.getProtocol());
// public String getHost( ) 获取该URL的主机名
System.out.println(url.getHost());
// public String getPort( ) 获取该URL的端口号
System.out.println(url.getPort());
// public String getPath( ) 获取该URL的文件路径
System.out.println(url.getPath());
// public String getFile( ) 获取该URL的文件名
System.out.println(url.getFile());
// public String getQuery( ) 获取该URL的查询名
System.out.println(url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
下载文件
public static void main(String[] args) {
HttpURLConnection urlConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL("http://localhost:8080/examples/beauty.jpg");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.connect();
is = urlConnection.getInputStream();
fos = new FileOutputStream("day10\\beauty3.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
System.out.println("下载完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
...省略了
}
}