Java网络通信
实现网络编程关键的三要素
**IP地址:**设备在网络中的地址,是唯一的标识。
**端囗:**应用程序在设备中唯一的标识。
**协议:**数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
IP地址
**IP(Internet Protocol):**全称”互联网协议地址”,是分配给上网设备的唯一标志。
**常见的IP分类为:**IPV4(4字节,32位)和IPV6(16字节,128位)。
IP地址的操作类
- 此类表示Internet协议(IP)地址。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-koKuWYwr-1660827824841)(C:\Users\Mazzz\AppData\Roaming\Typora\typora-user-images\image-20220816223115323.png)]
IP地址的代表类是谁?
- InetAddress类
2.如何获取本机IP对象?
- public static InetAddress getLocalHost()
3.如何判断与该IP地址对象是否互通?
- public boolean isReachable(int timegut)
端口
端口号
- 端口号:标识正在计算机设备上运行的进程(程序),被规定为一个16位的二进制,范围是0~65535。
端口类型
- 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21)
- 注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占用8080MySQL占用3306)
- 动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配
- 在我们开发的程序中,端口不能出现两个端口一样,否则会出错
协议
连接和通信数据的规则被称为网络通信协议
网络通信协议有两套参考模型
- OSI参考模型:世界互联协议标准,全球通信规范,由于此模型过于理想化,未能在因特网上进行广泛推广。
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
传输层的2个常见协议
- ·TCP(Transmission Control Protocol):传输控制协议
- ·UDP(User Datagram Protoco1):用户数据报协议
TCP协议特点(管道通信)
- 使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。
- 传输前,采用“三次握手”方式建立连接,所以是可靠的。
- 在连接中可进行大数据量的传输。
- 连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低。
TCP协议通信场景
对信息安全要求较高的场景,例如:文件下载、金融等数据通信。
TCP的三次握手
三次握手为客户端和服务器建立信任的过程
TCP四次回收断开连接
UDP协议
- UDP是一种无连接、不可靠传输的协议。
- 将数据源lP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64KB内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送,发送数据结束时无需释放资源,开销小,速度快
UDP协议通信场景
- 语音通话,视频会话等。
DatagramPacket:数据包对象
DatagramSocket:发送端和接收端对象
DatagramSocket类成员方法
1.UDP发送端和接收端的对象是哪个?
- public DatagramSocket():创建发送端的Socket对象
- public Datagramsocket(int port):创建接收端的Socket对象
2.数据包对象是哪个?
- DatagramPacket
3.如何发送、接收数据包
- 使用DatagramSocket的如下方法:
- public void send(DatagramPacket dp):发送数据包
- public void receive(DatagramPacket dp):接收数据包
UDP通信-广播、组播
UDP的三种通信方式
- 单播:单台主机与单台主机之间的通信。
- 广播:当前主机与所在网络中的所有主机通信。
- 组播:当前主机与选定的一组主机的通信。
UDP如何实现广播
- 使用广播地址:255.255.255.255
- 具体操作:
①发送端发送的数据包的目的地写的是广播地址、且指定端口。(255.255.255.255,9999)
②本机所在网段的其他主机的程序只要匹配端口成功即就可以收到消息了。(9999)
UDP如何实现组播
- 使用组播地址:224.0.0.0~239.255.255.255
- 真体操作:
①发送端的数据包的目的地是组播IP(例如:224.0.1.1,端口:9999)
②接收端必须绑定该组播IP(224.0.1.1),端口还要对应发送端的目的端口9999,这样即可接收该组播消息。
③DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP。
TCP
整体为创造在发送端和输出端的管道,用到了Socket对象
1.TCP通信的客户端的代表类是谁?
- Socket类
- public Socket(String host,int port)
2.TCP通信如何使用Socket管道发送、接收数据。
- Outputstream getoutputStream():获得字节输出流对象(发)
- Inputstream getinputstream():获得字节输入流对象(收)
Socket服务端
多线程下的TCP连接
代码1
package com.mazzz.net.TCP.multiCommunicate;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
String msg;
while ((msg = bufferedReader.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");;
}
}
}
代码2
package com.mazzz.net.TCP.multiCommunicate;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketDemo {
public static void main(String[] args) {
try {
System.out.println("===服务端已启动===");
ServerSocket serverSocket = new ServerSocket(7777);
while (true){
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "上线了!!!");
new ServerReaderThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码3
package com.mazzz.net.TCP.multiCommunicate;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class TcpDemo1 {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 7777);
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说!");
String msg = sc.nextLine();
ps.println(msg);
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
线程池
代码1
package com.mazzz.net.TCP.multiCommunicate;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class ServerDemoPool {
private static ExecutorService pool =new ThreadPoolExecutor(
3,
5,
6,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
public static void main(String[] args) {
try {
System.out.println("===服务端已启动===");
ServerSocket serverSocket = new ServerSocket(6666);
while (true){
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "上线了!!!");
Runnable target = new ServerReaderRunnable(socket);
pool.execute(target);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码2
package com.mazzz.net.TCP.multiCommunicate;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
String msg;
while ((msg = bufferedReader.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");;
}
}
}
代码3
package com.mazzz.net.TCP.multiCommunicate;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class TcpDemo1 {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 6666);
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说!");
String msg = sc.nextLine();
ps.println(msg);
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.本次使用线程池的优势在哪里?
- 服务端可以复用线程处理多个客户端,可以避免系统瘫痪。
- 适合客户端通信时长较短的场景。
2.即时通信是什么含义,要实现怎么样的设计?
- 即时通信,是指一个客户端的消息发出去,其他客户端可以接收到
- 即时通信需要进行端口转发的设计思想。
- 服务端需要把在线的Socket管道存储起来
- 一旦收到一个消息要推送给其他管道