一、InetAddress类
InetAddress是Java中用于封装IP地址的类。
获取本机的InetAddress对象:
InetAddress localHost = InetAddress.getLocalHost();
根据指定的主机名获取InetAddress对象(比如说域名)
InetAddress host = InetAddress.getByName("www.baidu.com");
通过InetAddress对象获取对应的地址
String hostAddress = host.getHostAddress();
通过InetAddress对象获取对应的主机名或者是域名
String hostAName = host.getHostName();
二、Socket
套接字(Socket)在开发网络应用程序中被广泛采用,以至于称为事实上的标准,它允许数据在两个Socket间通过IO传输。
流式套接字:
基于TCP协议,提供面向连接、可靠的数据传输服务。它保证数据的顺序性、完整性和可靠性,适用于对数据传输质量要求较高的场景,如文件传输、远程登录等。在Java中,使用Socket类来表示流式套接字。
数据报套接字:
基于UDP协议,提供无连接、不可靠的数据传输服务。它不保证数据的顺序性、完整性和可靠性,但传输速度快,适用于对实时性要求较高、对数据丢失不敏感的场景,如视频直播、在线游戏等。在Java中,使用DatagramScoket类和DatagramPacket类来实现数据报套接字通信。
一般来讲,主动发起通信的应用程序属于客户端,等待通信请求的为服务端。
三、TCP网络通信编程
3.1TCP套接字通信原理
3.1.1服务端
-
创建ServerSocket:服务器端首先创建一个ServerSocket对象,并绑定到一个特定的端口,等待客户端的连接请求。
ServerSocket ss = new ServerSocket(8888);
表示服务器端在本地的 8888 端口上创建一个ServerSocket,准备接收客户端的连接。
-
监听连接请求:服务器端通过ServerSocket的accept方法监听客户端的连接请求。当没有客户端发起连接请求时,accept方法会阻塞等待,直到接收到一个连接请求,然后返回一个新的Socket对象,该Socket对象代表了服务器端与客户端之间的连接。
Socket socket = ss.accept();
表示服务器端接受了一个客户端的连接请求,并创建了一个新的Socket对象来与该客户端进行通信。
-
数据传输:可以使用socket.getInputStream()获取输入流,用于接收客户端发送的数据;使用socket.getOutStream()获取输出流,用于向客户端发送数据。
-
关闭连接:数据传输完成后,服务器端需要关闭与客户端的连接,释放资源。
3.1.2客户端
- 创建Socket:客户端创建一个Socket对象,并指定服务器端的IP地址和端口号,发起连接请求。
Socket socket = new Socket(InetAddress.getLocalHost(),8888);
-
数据传输:连接建立后,客户端通过Socket对象的输入流和输出流与服务器端进行数据传输。与服务器端类似,客户端也可以使用socket.getInputStream和socket.OutputStream获取输入流和输出流,进行数据的读写操作。
-
关闭连接:数据传输完成后,客户端需要关闭连接,释放资源。
3.2TCP字节流编程实例
要求客户端向服务端发送hello serve,服务端受到后返回hello client。
服务端:
//1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket ss = new ServerSocket(8888);
System.out.println("客户端监听中");//如果没有客户端来连接,程序会阻塞等待连接
//如果有客户端连接则会返回socket对象,程序继续
Socket socket = ss.accept();
System.out.println("客户端IP" + socket.getInetAddress().getHostAddress());//2.通过socket.getInputstream()读取客户端写入到数据通道的数据
InputStream inputstream = socket.getInputStream();//IO读取
int readln = 0;
byte[] bytes = new byte[1024];
while((readln = inputstream.read(bytes)) != -1){System.out.println(new String(bytes,0,readln));
}
socket.shutdownInput();//3.通过socket.getOutputStream()给客户端发送数据
OutputStream outputstream = socket.getOutputStream();
outputstream.write("hello client".getBytes());//4.关闭流和socket
inputstream.close();
outputstream.close();
socket.close();
System.out.println("服务端退出");
客户端:
//1.连接服务端
Socket socket = new Socket(InetAddress.getLocalHost(),8888);
System.out.println("服务端IP" + socket.getInetAddress().getHostAddress());//2.连接上后,生成Socket,通过socket.getOutputstream()得到与socket对象相关连的输出流对象
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
//通过输出流,写入数据到数据通道
outputStream.write("hello serve".getBytes());
//设计结束标记
socket.shutdownOutput();//通过输入流,从通道中读取数据
byte[] buf = new byte[1024];
int readln = 0;
while ((readln = inputStream.read(buf)) != -1) {System.out.println(new String(buf, 0, readln));
}//3.关闭流和对象
outputStream.close();
inputStream.close();
socket.close();
System.out.println("客户端已关闭");
3.3TCP文件上传
3.3.1一个工具类
public class StreamUtils {/*** 功能:将输入流转换成byte[]* @param is* @return* @throws Exception*/public static byte[] streamToByteArray(InputStream is) throws Exception{ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象byte[] b = new byte[1024];int len;while((len=is.read(b))!=-1){bos.write(b, 0, len); }byte[] array = bos.toByteArray();bos.close();return array;}/*** 功能:将InputStream转换成String* @param is* @return* @throws Exception*/public static String streamToString(InputStream is) throws Exception{BufferedReader reader = new BufferedReader(new InputStreamReader(is));StringBuilder builder= new StringBuilder();String line;while((line=reader.readLine())!=null){ //当读取到 null时,就表示结束builder.append(line+"\r\n");}return builder.toString();}}
只需要知道这里的streamToByteArray方法将输入流的数据转换为byte[],streamToString方法将输入流转换为String。
3.3.2思路分析
先将磁盘上的图片转换成文件字节数组,通过Socket传输给服务端,服务端将得到的bytes文件写入到指定的路径中,向客户端回复 收到图片。
3.3.3服务端
//1.服务端在本机监听8888端口
ServerSocket ss = new ServerSocket(8888);
System.out.println("服务端在8888端口监听");//2.等待连接
Socket socket = ss.accept();//3.读取客户端发送的消息
//通过Socket得到输入流BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);//4.将得到的bytes数组写入到指定的路径中,就得到一个文件了
String str = "src\\my.jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(str));
bos.write(bytes);
bos.close();//向客户端回复收到图片
//通过socket获取到输出流(字符)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("收到图片");
bw.flush();
socket.shutdownOutput();
//关闭其它资源
bw.close();
bis.close();
socket.close();
ss.close();
3.3.4客户端
//1.客户端连接服务端8888,得到Socket对象
Socket socket = new Socket(InetAddress.getLocalHost(),8888);//创建读取磁盘文件的输入流
String filePath = "D:\\My.jpg";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));//byte就是filePath对应的字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);//通过socket获取到输出流,将bytes数据发送给服务端
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);
bis.close();
socket.shutdownOutput();//接收从服务端回复的消息
InputStream inputStream = socket.getInputStream();
//使用StreamUtils的方法直接将inputStream读取到的内容转成字符串
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);//关闭相应的流
inputStream.close();
bos.close();
socket.close();
3.4TCP文件下载
3.4.1思路分析
客户端将要下载的文件名传递给服务端,服务端返回对应文件,如果没有就返回默认。
3.4.2服务端
//1.监听 9999端口
ServerSocket serverSocket = new ServerSocket(9999);//2.等待端口连接
Socket socket = serverSocket.accept();//读取客户端发送要下载的文件名
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
int len = 0;
String downLoadFileName = "";
while ((len = inputStream.read(b)) != -1) {downLoadFileName += new String(b,0,len);
}
System.out.println("用户希望下载的文件名=" + downLoadFileName);String resFileName = "";
if("高山流水".equals(downLoadFileName)){resFileName = "src\\高山流水.mp3";
}
else resFileName = "src\\无名.mp3";
//4.创建一个输入流,读取文件
BufferedInputStream bis =new BufferedInputStream(new FileInputStream(resFileName));//5.使用工具类StreamUtils,读取文件到一个字节数组
byte[] buffer = StreamUtils.streamToByteArray(bis);//6.得到Socket相关的输出流
BufferedOutputStream bos =new BufferedOutputStream(socket.getOutputStream());//写入到数据通道,返回给客户端
bos.write(buffer);
socket.shutdownOutput();//关闭相关资源
bos.close();
bis.close();
socket.close();
serverSocket.close();
System.out.println("服务端退出");
3.4.3客户端
//1.接收用户输入,指定下载文件名
Scanner sc = new Scanner(System.in);
System.out.println("请指定下载文件名");
String downloadFileName = sc.nextLine();//2.客户端连接服务端,准备发送
Socket socket = new Socket(InetAddress.getLocalHost(),9999);//3.获取关联输出流
OutputStream os = socket.getOutputStream();
os.write(downloadFileName.getBytes());
//设置写入结束标志
socket.shutdownOutput();//4.读取服务端返回的文件
BufferedInputStream bis =new BufferedInputStream(socket.getInputStream());
byte[] buffer = StreamUtils.streamToByteArray(bis);//5.得到一个输出流,准备将bytes写入到磁盘文件
String filePath = "D:\\" + downloadFileName + ".mp3";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bos.write(buffer);//关闭相关资源
bos.close();
bis.close();
os.close();
socket.close();
System.out.println("客户端已退出");
四、UDP网络通信编程
4.1 UDP通信原理
4.1.1发送端
- 创建DatagramSocket:发送端创建一个DatagramSocket对象,用于发送数据报。可以不指定端口号,让系统自动分配一个临时端口号;也可以指定一个端口号。
DatagramSocket socket = new DatagramSocket(); DatagramSocket socket = new DatagramSocket(8888);
- 创建DatagramPacket:发送端创建一个DatagramPacket对象,用于封装要发送的数据。需要指定数据内容、数据长度、接收端的 IP 地址和端口号。
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("server_ip"), 8888);
- 发送数据报:通过DatagramSocket的send(DatagramPacket p)方法发送数据报。
- 关闭DatagramSocket:数据发送完成后,发送端关闭DatagramSocket,释放资源。
4.1.2接收端
- 创建DatagramSocket:接收端创建一个DatagramSocket对象,并绑定到一个特定的端口上,用于接收数据报。
DatagramSocket socket = new DatagramSocket(8888);
- 接收数据报:接收端通过DatagramSocket的reveive(Datagrampacket p)方法接收数据报。需要先创建一个Datagrampacket 对象作为接收容器,指定一个足够大的缓冲区来存储接收到的数据。
byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet);
- 处理数据:接收到数据报后,可以通过Datagrampacket对象获取数据内容、发送端的 IP 地址和端口号等信息。
String receivedData = new String(packet.getData(), 0, packet.getLength()); InetAddress senderAddress = packet.getAddress(); int senderPort = packet.getPort();
- 关闭DatagramSocket:数据处理完成后,接收端关闭DatagramSocket,释放资源。
4.2UDP网络编程
4.2.1A端
//1.创建一个DatagramSocket对象,准备在9999端口接收数据
DatagramSocket socket = new DatagramSocket(9999);
//2.构建一个DatagramPacket对象,准备接收数据
//UDP协议一个数据包最大64k
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);//3.调用接收方法,将通过网络传输的DatagramPacket对象
// 填充到packet对象中
//如果没有数据包发送到本机的9999端口,就会阻塞等待socket.receive(packet);//4.可以把packet拆包取出数据,并显示
int len = packet.getLength();//实际接收到的数据字节长度
byte[] data = packet.getData();//接收到数据
String s = new String(data,0,len);
System.out.println(s);//5.回复
byte[] buf2 = "好的".getBytes();
DatagramPacket packet2 = new DatagramPacket(buf2, buf2.length, InetAddress.getByName("192.168.31.130"),8888);
socket.send(packet2);
//关闭资源
socket.close();
System.out.println("A端退出");
4.2.2B端
//1.创建DatagramSocket对象,准备在8888端口接收数据
DatagramSocket socket = new DatagramSocket(8888);//2.将需要发送的数据封装到DatagramPacket对象中
byte[] data = "hello 明天吃火锅".getBytes();//封装的DatagramPacket对象: data内容字节数组,data.length,主机IP,端口
DatagramPacket datagramPacket =new DatagramPacket(data, data.length, InetAddress.getByName("192.168.31.130"),9999);
socket.send(datagramPacket);//接收A端的回复
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
//拆包
int len = receivePacket.getLength();
byte[] receiveData2 = receivePacket.getData();
String s = new String(receiveData2,0,len);
System.out.println(s);
//关闭资源
socket.close();
System.out.println("B端退出");