网络编程概念
- 可以让设备中的程序与网络上其他设备中的程序进行数据交互的技术(实现网络通信)
- 基本的通信架构分为两种:CS架构(Client客户端/Server服务端)、BS架构(Browser浏览器/Server服务端)
- 无论是CS架构还是BS架构,都必须依赖网络编程
- Java中提供了
java.net.*包用于进行网络编程
网络编程三要素
三要素为:IP地址、端口、协议
- IP地址:设备在网络中的地址,是设备在网络中的唯一标识
- 端口:应用程序在设备中的唯一标识
- 协议:连接和数据在网络中传输的规则
IP地址
- IP(Internet Protocol):全称“互联网那个协议地址”,是分配给上网设备的唯一标识
- 目前,被广泛采用的IP地址形式有两种:IPv4、IPv6
IPv4:是Internet Protocol Version 4的缩写,它使用32位地址,通常以点分十进制表示,如192.168.100.200
IPv6:是Internet Protocol Version 6的缩写,它使用128为地址。IPv6分为8段,每段每四位编码成一个十六进制表示,每段之间用冒号分开。这种形式又称为冒分十六进制,比如:2001:0db8:0000:0023:0008:0800:200c:417a
端口号
由两个字节表示的整数,取值范围:0~65535
其中,0~1023之间的端口号用于一些知名的网络服务或者应用。开发中一般都使用1024或以上的端口号
注意:端口号只能被一个应用程序使用
协议
UDP协议:
- 用户数据包协议(User Datagram Protocol)
- UDP面向无连接通信协议。速度快,有大小限制一次最多发送64K,数据不安全,易丢失(只管发送,不管对方是否接收成功)
TCP协议:
- 传输控制协议(Transmission Control Protocol)
- TCP协议是面向连接的通信协议。速度慢,没有大小限制,数据安全(发送后还要确认对方是否接收成功)
网络编程
InetAddress对象
为了方便获取和操作IP地址,Java提供了一个类InetAddress,此类表示Internet协议(IP)地址
| 方法名 |
说明 |
|
| static InetAddress getByName(String host) |
确定主机名称的IP地址 主机名称可以是机器名称,也可以是IP地址 |
|
| String getHostName() |
获取此IP地址的主机名 |
|
| String getHostAddress() |
返回文本显示中的IP地址字符串 |
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package net;
import java.net.InetAddress; import java.net.UnknownHostException;
public class InetAddressDemo01 { public static void main(String[] args) throws UnknownHostException { InetAddress address = InetAddress.getByName("192.168.190.129"); System.out.println(address); String hostName = address.getHostName(); System.out.println(hostName);
String hostAddress = address.getHostAddress(); System.out.println(hostAddress); } }
|
UDP收发数据
服务端(发送端)总体流程:
- 创建发送端DatagramSocket对象,指定发送端口
- 数据打包(DatagramPacket)
- 发送数据
- 释放资源
发送端示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package net;
import java.io.IOException; import java.net.*;
public class UDPSend01 { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(8888);
String string = "Hello World"; byte[] bytes = string.getBytes();
DatagramPacket dp = new DatagramPacket(bytes,bytes.length, InetAddress.getByName("127.0.0.1"),9999);
ds.send(dp);
ds.close(); } }
|
客户端(接收端)总体流程:
- 创建DatagramSocket对象,指定接收端口
- 创建DatagramPacket对象准备接收数据
- 接收数据,程序会在这一步阻塞运行,直到接收到数据
- 对包裹进行拆封,获取真实数据
- 释放资源
接收端示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package net;
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.util.Arrays;
public class UDPReceive01 { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(9999);
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
ds.receive(dp);
byte[] data = dp.getData(); String string = new String(data, 0, dp.getLength()); String hostAddress = dp.getAddress().getHostAddress(); System.out.println("接收到" + hostAddress + "发送的" + string);
ds.close(); } }
|
TCP收发数据
客户端流程如下:
- 创建Socket对象指定ip和端口:
Socket(String host,int port);
- 通过socket对象获取传输数据的流对象:
OutputStream getOutputStream();或InputStream getInputStream();
- 通过流对象收发数据
- 释放资源
这里的流对象不是读写文件中的数据
输出流:写出数据到服务端
输入流:读取服务端发送的数据
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package net;
import java.io.*; import java.net.Socket;
public class TCPSend01 { public static void main(String[] args) throws IOException { File src = new File("C:\\Users\\Zhao\\Desktop\\test01.png");
Socket socket = new Socket("127.0.0.1",19999);
InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(src); byte[] bytes = new byte[1024]; int len; while ((len = fis.read(bytes)) != -1) { os.write(bytes,0,len); } socket.shutdownOutput(); fis.close();
byte[] receiveData = new byte[1024]; int receiveDataLen = is.read(receiveData); String msg = new String(receiveData,0,receiveDataLen);
System.out.println("读取到服务端发送过来的消息为:" + msg);
is.close(); os.close(); socket.close(); } }
|
Socket中指定的是服务端的IP地址+端口号
当数据写出完成后,应该向服务端发送完成标记(socket调用shutdownOutput方法),否则服务端会认为客户端还有数据需要发送,就不会停止服务端的写入操作
记得读取服务端发送过来的响应数据(比如数据接收成功这类的消息)
服务端流程如下:
- 创建ServerSocket对象指定端口:
ServerSocket(int port);
- 响应客户端发送的请求:
Socket accpet();
- 通过socket对象获取传输数据的流对象:
OutputStream getOutputStream();或InputStream getInputStream();
- 通过流对象收发数据
- 释放资源
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package net;
import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;
public class TCPReceive01 { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(19999);
Socket socket = server.accept(); System.out.println("响应成功");
InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream();
byte[] bytes = new byte[1024]; int len; FileOutputStream fos = new FileOutputStream("E:\\test01.png"); while ((len = is.read(bytes)) != -1) { fos.write(bytes,0,len); } fos.close();
os.write("上传成功!!!".getBytes());
is.close(); os.close(); socket.close(); server.close(); } }
|
服务端相比客户端,需要单独创建一个ServerSocket对象
如果客户端没有发送完成标记,那么程序会卡在循环读取客户端发送的数据这一步
利用OutputStream可以向客户端发送响应信息