Java-网络编程
网络通信协议:
网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocol/Internet Protocol,传输控制协议/英特网互联协议)、UDP协议(User Datagram Protocol,用户数据报协议)和一些其他的协议。下面我们学习的协议,主要是基于TCP/IP协议中的内容。
基于TCP/IP的参考模型将协议分成四个层次,如下图所示:

TCP/IP协议中的一共有四层,分别是链路层、网络层、传输层和应用层。
实现网络通信,需要通信双方的地址:ip、端口号等数据。
一般的网络通信协议:http、ftp、smtp、tcp、udp等。
TCP/IP参考模型:
网络编程中的要素:ip和端口号;网络通信协议
- 在JDK中,提供了一个与IP地址相关的InetAddress类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法:
//测试IP:
public class IP {
public static void main(String[] args) {
try {
//查询本机IP地址:
InetAddress InetAddress1 = InetAddress.getByName("192.168.239.1");
System.out.println(InetAddress1);
System.out.println("获取本地主机地址:" + InetAddress.getByName("localhost"));
System.out.println("获取本机IP地址:" + InetAddress.getLocalHost());
//查询网站IP地址:
InetAddress InetAddress2 = InetAddress.getByName("www.bilibili.com");
System.out.println(InetAddress2);
System.out.println(InetAddress2.getAddress());
System.out.println(InetAddress2.getHostName());
System.out.println(InetAddress2.getCanonicalHostName());
System.out.println(InetAddress2.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
//查询端口号:
public class TestInetSocketAddress {
public static void main(String[] args) {
InetSocketAddress inetSocketAddress1 = new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost",8080);
System.out.println(inetSocketAddress1);
System.out.println(inetSocketAddress2);
System.out.println(inetSocketAddress1.getPort());
System.out.println(inetSocketAddress1.getAddress());
System.out.println(inetSocketAddress1.getHostName());
System.out.println(inetSocketAddress1.getHostString());
}
}
通信协议:
在介绍TCP/IP结构时,提到传输层的两个重要的高级协议,分别是UDP和TCP。
- UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。例如:发短信;
- TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。;例如:打电话;
TCP:用户传输协议
TCP通信同UDP通信一样,也能实现两台计算机之间的通信,但TCP通信的两端需要创建socket对象。而且服务端必须先启动,再启动客户端;
在JDK中提供了两个用于实现TCP程序的类,一个是ServerSocket类,用于表示服务器端;一个是Socket类,用于表示客户端。通信时,首先要创建代表服务器端的ServerSocket对象,创建该对象相当于开启一个服务,此服务会等待客户端的连接;然后创建代表客户端的Socket对象,使用该对象向服务器端发出连接请求,服务器端响应请求后,两者才建立连接,开始通信。
- 三次握手,四次挥手(最少保持三次数据回应,保证稳定连接)
- 客户端、服务端
- 传输完成,当即释放链接,效率低
TCP测试:
1)客户端:
-
连接服务器
Socket -
发送消息
2)服务端:
-
建立服务的端口
ServerSocket -
等待用户连接
accept -
接收用户消息
//测试一:
//客户端
public class TcpClientDemo01 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
//1、获取服务器的端口号:
// InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
// int port = 9999;
//2、创建一个socket连接:
// socket = new Socket(inetAddress,port);
socket = new Socket("127.0.0.1",9999);
// Socket socket1 = new Socket();
// socket1.connect(new InetSocketAddress("127.0.0.1",9999));
//3、发送消息IO:
os = socket.getOutputStream();
os.write("this is socket message!".getBytes());
System.out.println("message ==> server");
} catch (Exception e) {
e.printStackTrace();
}finally {
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//服务端
public class TcpServerDemo01 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream bais = null;
//服务端需要设置一个端口号:ServerSocket()
try {
//1、设置一个地址:
serverSocket = new ServerSocket(9999);
//2、客户端连接过来:accept()
socket = serverSocket.accept();
//3、收取消息:(获取输入流)
is = socket.getInputStream();
//定义管道流写出数据: ByteArrayOutputStream() 不会发生字符乱码现象;
bais = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int leng;
while ((leng = is.read(bytes)) != -1){
bais.write(bytes,0,leng);
}
System.out.println(bais.toString());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bais != null){
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//测试二:
//客户端:
public class TcpClientDemo02 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
FileInputStream fis = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1、连接到服务端:
socket = new Socket(InetAddress.getByName("127.0.0.1"),9000);
//2、输出文件到服务端:
os = socket.getOutputStream();
fis = new FileInputStream(new File("img1.jpg"));
byte[] bytes = new byte[1024];
int leng;
while ((leng = fis.read(bytes)) != -1){
os.write(bytes,0,leng);
}
//通知服务器已经发送完毕:
socket.shutdownOutput(); //传输完毕了
//3、确定服务器接收完毕,断开连接:
is = socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] bytes1 = new byte[1024];
int leng1;
while ((leng1 = is.read(bytes1)) != -1){
baos.write(bytes1,0,leng1);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//服务端:
public class TcpServerDemo02 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
FileOutputStream fos = null;
try {
//1、设置服务端的端口号:
serverSocket = new ServerSocket(9000);
//2、连接到客户端:(阻塞式监听,会一直等待客户端连接)
socket = serverSocket.accept();
//3、接收客户发送的信息:
is = socket.getInputStream(); //输入
fos = new FileOutputStream("clientimg2.jpg"); //输出
byte[] bytes = new byte[1024];
int leng;
while ((leng = is.read(bytes)) != -1){
fos.write(bytes,0,leng);
}
//4、通知客户端接收完毕:
OutputStream os = socket.getOutputStream();
os.write("the message over".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
UDP:用户数据报协议
UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。
UDP通信发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。其中发送端可以使用send()发送包,接收端可以使用receive()接收包。
- 不需要连接,不稳定
- 客户端、服务端:没有明确的界限
- 无需对面用户准备好,即发即收
- 发送数据结束时无需释放资源,开销小,速度快
//不需要连接服务器,可以直接发送消息:DatagramSocket()、DatagramPacket()
//发送端:
public class UdpClientDemo01 {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
//1、建立一个Socket:
socket = new DatagramSocket();
//2、建立包:
String msg = "服务器!服务器!";
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
//需要发送的数据,数据的起始位置,数据的结束位置,ip地址,端口号
DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,localhost,port);
//3、发送包:
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null){
socket.close();
}
}
}
}
//接收端:
public class UdpServerDemo01 {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
//开放端口:
socket = new DatagramSocket(9090);
//接收数据包:
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length);
//阻塞接收:
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData()));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null){
socket.close();
}
}
}
}
//测试:定义从控制台输入数据到接收端的程序:
//发送端:
public class UdpChatSend {
public static void main(String[] args) throws Exception {
//1、建立一个Socket:
DatagramSocket socket = new DatagramSocket();
//2、定义发送的数据: 从控制台读取数据
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
String str = bfr.readLine();
while (true){
DatagramPacket packet = new DatagramPacket(str.getBytes(),0,str.getBytes().length,
new InetSocketAddress("localhost",6666));
socket.send(packet);
if ("bye".equals(str)){ //如果输入bye,则会退出
break;
}
}
bfr.close();
socket.close();
}
}
//接收端:
public class UdpChatReceive {
public static void main(String[] args) throws Exception {
//1、开发端口:
DatagramSocket socket = new DatagramSocket(6666);
//2、接收数据包:
while (true){
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length);
socket.receive(packet); //阻塞接收数据
//获取接收的数据:
byte[] data = packet.getData();
String datas = new String(data,0,data.length);
//打印输出传输过来的数据:
System.out.println(datas);
//断开连接:
if (datas.trim().equals("bye")){
break;
}
}
socket.close();
}
}
URL类
URL:统一资源定位符,他表示Internet上某一资源的地址。
URL的基本结构由5部分组成:
<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
例如:http://localhost:8080/vx/doc.jsp?username=vx&passwd=123#a
!!:片段名即锚点,例如浏览淘宝,直接定位到第几页。
参数列表格式:参数名=参数值&参数名=参数值.....
在JDK中,URL提供了以下方法:
public class TestUrl {
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:8080/vx/doc.jsp?username=vx&passwd=123#a");
System.out.println("获取与此url关联的协议的默认端口:" + url.getDefaultPort());
System.out.println("端口号后面的内容:" + url.getFile());
System.out.println("主机名:" + url.getHost());
System.out.println("路径:" + url.getPath()); // 端口号后,参数前的内容
System.out.println("端口:" + url.getPort());
System.out.println("协议:" + url.getProtocol());
System.out.println("参数部分:" + url.getQuery());
System.out.println("锚点:" + url.getRef());
}
}
//使用URL下载文件在存储到本地:
public class TestUrlP {
public static void main(String[] args) throws Exception {
//1、下载地址:
URL url = new URL("需要下载的网络资源地址");
//2、连接到这个资源:
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//获取连接:
connection.connect();
//3、输入写出流:
InputStream is = connection.getInputStream();
FileOutputStream fos = new FileOutputStream("保存资源名字.后缀");
byte[] bytes = new byte[1024];
int leng;
while ((leng = is.read(bytes)) != -1){
fos.write(bytes,0,leng);
}
//3、关闭流
fos.close();
is.close();
connection.disconnect();
}
}

浙公网安备 33010602011771号