三要素
IP地址:表示设备在网络中的地址,是网络中设备的唯一标识
端口号:应用程序在设备中唯一的标识
协议:连接和数据在网络中传输的规则
InetAddress类
UDP
UDP是面向无连接的、不需要确认双方是否存在,所以它是不可靠的协议。Java提供了一个类叫DatagramSocket来完成基于UDP协议的收发数据。使用DatagramSocket收发数据时,数据要以数据包的形式体现,一个数据包限制在64KB以内。
DatagramPacket数据包的意思。通信双方都需要有DatagramSocket(理解为发数据的人),还需要有DatagramPacket(可以理解为装数据的东西)。
一发一收
public class Client {
public static void main(String[] args) throws Exception {
// 1、创建客户端对象(发韭菜出去的人)
DatagramSocket socket = new DatagramSocket(7777);
// 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)
byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length
, InetAddress.getLocalHost(), 6666);
// 3、开始正式发送这个数据包的数据出去了
socket.send(packet);
System.out.println("客户端数据发送完毕~~~");
socket.close(); // 释放资源!
}
}
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("----服务端启动----");
// 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口
DatagramSocket socket = new DatagramSocket(6666);
// 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)
byte[] buffer = new byte[1024 * 64]; // 64KB.
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 3、开始正式使用数据包来接收客户端发来的数据
socket.receive(packet);
// 4、从字节数组中,把接收到的数据直接打印出来
// 接收多少就倒出多少
// 获取本次数据包接收了多少数据。
int len = packet.getLength();
String rs = new String(buffer, 0 , len);
System.out.println(rs);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
socket.close(); // 释放资源
}
}
多发多收
建立while死循环,里面设置条件结束通信。
TCP
三次握手
四次挥手
一发一收/多发多收
当创建Socket对象时,就会在客户端和服务端创建一个数据通信的管道,在客户端和服务端两边都会有一个Socket对象来访问这个通信管道。
现在假设客户端要发送一个“在一起”给服务端,客户端这边先需要通过Socket对象获取到一个字节输出流,通过字节输出流写数据到服务端
然后服务端这边通过Socket对象可以获取字节输入流,通过字节输入流就可以读取客户端写过来的数据,并对数据进行处理。
服务端处理完数据之后,假设需要把“没感觉”发给客户端端,那么服务端这边再通过Socket获取到一个字节输出流,将数据写给客户端
客户端这边再获取输入流,通过字节输入流来读取服务端写过来的数据。
以下示例为一发一收,多发多收只需在业务代码套一层循环即可。
public class Client {
public static void main(String[] args) throws Exception {
// 1、创建Socket对象,并同时请求与服务端程序的连接。
Socket socket = new Socket("127.0.0.1", 8888);
// 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
OutputStream os = socket.getOutputStream();
// 3、把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
// 4、开始写数据出去了
dos.writeUTF("在一起,好吗?");
dos.close();
socket.close(); // 释放连接资源
}
}
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("-----服务端启动成功-------");
// 1、创建ServerSocket的对象,同时为服务端注册端口。
ServerSocket serverSocket = new ServerSocket(8888);
// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流。
InputStream is = socket.getInputStream();
// 4、把原始的字节输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
// 5、使用数据输入流读取客户端发送过来的消息
String rs = dis.readUTF();
System.out.println(rs);
// 其实我们也可以获取客户端的IP地址
System.out.println(socket.getRemoteSocketAddress());
dis.close();
socket.close();
}
}
多线程改进
服务端读取数据线程类
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
} catch (Exception e) {
System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
改写服务端主程序
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("-----服务端启动成功-------");
// 1、创建ServerSocket的对象,同时为服务端注册端口。
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
// 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
new ServerReaderThread(socket).start();
}
}
}