网络通信的关键三要素
IP、端口号、协议
IP地址
IP地址(Internet Protocol):全程“互联网协议地址”,是分配给上网设备的唯一标志。
IP地址有两种形式:IPv4、IPv6
InetAddress
代表IP地址
InetAddress 的常用方法如下
名称 | 说明 |
---|---|
public static InetAdress getLocalHost() | 获取本机IP,会以一个inetAddress 的对象返回 |
public static InetAddress getByName(String host) | 根据ip地址或者域名,返回一个inetAddress对象 |
public String getHostName() | 获取该IP地址对象对应的主机名。 |
public String getHostAddress() | 获取该IP地址对象中的ip地址信息 |
public boolean isReachable(int timeout) | 在指定毫秒内,判断主机与该ip对应主机是否能够联通 |
import java.net.InetAddress;
import java.sql.SQLOutput;
public class InetAddresstest {
public static void main (String[] args )throws Exception
{
//1、获取本机IP地址对象
InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());
//2、获取指定IP或者域名的IP地址对象
InetAddress ip2=InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress());
System.out.println(ip2.isReachable(6000));
}
}
端口号
标记正在设备上运行的应用程序的,被规定为一个16位的二进制,范围是0~65535
分类
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序。
动态端口:49151到65535,之所以被称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则会出错。
通信协议
网络上通信设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
开放式互联网络互联标准:OSI 网络参考模型
OSI网络参考模型:全球网络互联标准
TCP/IP网络模型:事实上的国际标准
传输层的两个通信协议
UDP(User Datagram Protocol):用户数据报协议;
TCP(Transmission Control Protocol) :传输控制协议
UDP
特点:无连接、不可靠通信
不事先建立连接,数据按照包发,一包数据含:自己的IP、程序端口、目的IP、程序端口和数据(限制在64KB内)
发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,是不可靠的。
TCP
特点:面向连接、可靠通信。
TCP的最终目的:要保证在不可靠的信道上实现可靠传输。
TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。
UDP通信
特点:无连接、不可靠通信
不事先建立连接;发送端每次要把发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发不出去就算了。
Java提供了一个Java.net.DatagramSocket类来实现UDP通信。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.sql.SQLOutput;
public class Server {
public static void main(String[] args) throws Exception
{
//1、创建一个服务对象(创建一个接韭菜的人)注册端口
DatagramSocket socket =new DatagramSocket(
6666);
//2、创建一个数据包对象,用于接受数据的(创建一个韭菜盘子)
byte[] buffer = new byte[1024*64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
//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());
System.out.println("----------------");
}
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception{
//1、创建客户端对象
DatagramSocket socket = new DatagramSocket();
//2、创建数据包对象封装要发出去的数据(创建一个韭菜盒子)
//public DatagramPacket(byte buf[],int length,InetAddress address,int port)
//InetAddress address,int port)
//参数1:封装要发出去的数据。
//参数2:发出去的数据大小(字节个数)
//参数3:服务器额的IP地址
//参数4:服务程序的端口
Scanner sc= new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg= sc.nextLine();
if("exit".equals(msg)) {
System.out.println("欢迎下次光临!推出成功!");
socket.close();
break;
}
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),6666);
//3、正式发送这个数据包的数据出去了
socket.send(packet);
}
}
}
TCP通信
特点:面向连接、可靠通信
通信方面事先会采用”方式建立可靠连接“,实现端到端的通信;底层能保证数据成功传给服务器
java提供了一个Java.net.Socket类来实现TCP通信。
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLOutput;
public class Server {
public static void main(String[] args) throws Exception
{
//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();
}
}
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
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();
}
}
下面是一个简单的Java程序,用于实现多个客户端同时通信:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MultiClientServer {
private List<Socket> clients = new ArrayList<>();
public static void main(String[] args) {
MultiClientServer server = new MultiClientServer();
server.start();
}
public void start() {
try {
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("Server started");
while (true) {
Socket clientSocket = serverSocket.accept();
clients.add(clientSocket);
ClientHandler clientHandler = new ClientHandler(clientSocket);
Thread clientThread = new Thread(clientHandler);
clientThread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private class ClientHandler implements Runnable {
private Socket clientSocket;
private BufferedReader reader;
private PrintWriter writer;
public ClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
try {
reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
writer = new PrintWriter(clientSocket.getOutputStream(), true);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
String message;
while ((message = reader.readLine()) != null) {
System.out.println("Received message: " + message);
// 向所有客户端发送消息
for (Socket client : clients) {
PrintWriter clientWriter = new PrintWriter(client.getOutputStream(), true);
clientWriter.println(message);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
这个程序创建了一个服务器端,可以同时处理多个客户端的连接。当一个客户端发送消息时,服务器会将该消息转发给所有连接的客户端。
运行这个程序后,可以使用多个客户端程序连接到服务器,并进行通信。每个客户端发送的消息都会被服务器广播给所有连接的客户端。
注意:这个程序是一个简单的示例,没有处理异常情况和并发安全性。在实际使用中,应该对异常进行处理并确保多个线程之间的安全访问。