一、Java网络编程
网络编程是指编写运行在多个设备(计算机)的程序,设备通过网络连接起来。java.net 包中 J2SE 的 API 包含有类和接口,提供了低层次的通信细节。可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。
协议:计算机网络中,连接和通信的规则被称为网络通信协议
1.UDP协议
- 用户数据报协议(User Datagram Protocol)
- UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据
- 由于使用UDP协议消耗资源少,通信效率高,所以通常都会用于音频、视频和普通数据的传输
- 例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议
2.TCP协议
- 传输控制协议(Transmission Control Protocol)
- TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据出啊u念书。在TCP连接中必须明确客户端与服务器端,由于客户端向服务器端发出连接请求,每次连接的创建都需要经过“三次握手”
3.三次握手
- TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
- 第一次握手:客户端向服务器端发出连接请求,等待服务器确认
- 第二次握手:服务器端向客户端回送一个响应,通知客户端收到了连接请求
- 第三次握手:客户端再次向服务器端发送确认信息,确认连接
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用非常广泛。例如上传文件、下载文件、浏览网页
二、Java中InetAddress的使用
Java具有较好的网络编程模型/库,其中非常重要的一个API便是InetAddress。在在java.net网络编程中中有许多类都使用到了InetAddress,包括ServerSocket,Socket,DatagramSocket等等。
Java提供InetAddress类(有Inet4Address和Inet6Address两种实现),可以对域名-IP进行正向、逆向解析。InetAddress解析的时候一般是调用系统自带的DNS程序,比如:Linux下默认使用哪个DNS去解析以及其规则是由/etc/resolv.conf该文件制定
IP地址是IP使用的32位(IPv4)或者128位(IPv6)位无符号数字,它是传输层协议TCP,UDP的基础。InetAddress是Java对IP地址的封装。
java.net.IntAddress类是Java对IP地址的高层表示。大多数其它网络类都要用到这个类,包括Socket、ServerSocket、URL、DatagramSocket、DatagramPacket等。InetAddress的实例对象包含了IP地址,同时还可能包含主机名(如果使用主机名来获取InetAddress的实例,或者使用数字来构造,并且启用了反向主机名解析的功能)。InetAddress类提供了将主机名解析为IP地址(或反之)的方法。
InetAddress对域名进行解析是使用本地机器配置(如域名系统DNS和网络信息服务(Network Information Service,NIS))来实现。本地需要向DNS服务器发送查询的请求,然后服务器根据一系列的操作,返回对应的IP地址,为了提高效率,通常本地会缓存一些主机名与IP地址的映射,这样访问相同的地址,就不需要重复发送DNS请求了。如下:
public class Test01 {
public static void main(String[] args) throws UnknownHostException {
//InetAddress ia = new InetAddress();不能直接创建对象,因为InetAddress()被default修饰了。
/*InetAddress ia = InetAddress.getByName("192.168.200.114");
System.out.println(ia);
InetAddress ia2 = InetAddress.getByName("localhost");//localhost指代的是本机的ip地址
System.out.println(ia2);
InetAddress ia3 = InetAddress.getByName("127.0.0.1");//127.0.0.1指代的是本机的ip地址
System.out.println(ia3);
InetAddress ia4 = InetAddress.getByName("DESKTOP-A5TIRM1");//封装计算机名
System.out.println(ia4);
InetAddress ia5 = InetAddress.getByName("www.augus.com");//封装域名
System.out.println(ia5);*/
// InetSocketAddress ---》封装了IP,端口号
InetSocketAddress inetSocketAddress = new InetSocketAddress("192.168.200.114", 9999);
System.out.println(inetSocketAddress); ///192.168.200.114:9999
System.out.println(inetSocketAddress.getHostName()); //输出计算机的名称 DESKTOP-A5TIRM1
System.out.println(inetSocketAddress.getPort());//输出端口
InetAddress ia1 = inetSocketAddress.getAddress();
System.out.println(ia1.getHostName());//输出计算机的名称 DESKTOP-A5TIRM1
System.out.println(ia1.getHostAddress());//192.168.200.114
}
}
三、基于TCP的网络编程
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。
- 使用基于TCP协议的Socket网络编程实现,使用Socket对象来代表两端的通信端口
- TCP协议基于请求-响应模式,第一次主动发起的程序被称为客户端(Client)程序
- 第一次通讯中等待连接的程序被称为服务器端(Sercer)程序
利用IO流实现数据的传输
原理说明及详细步骤
- 在服务端指定一个端口号来创建ServerSocket,并使用accept方法进行侦听,这将阻塞服务器线程,等待用户请求。
- 在客户端指定服务的主机IP和端口号来创建socket,并连接服务端ServerSocket,此时服务端accept方法被唤醒,同时返回一个和客户端通信的socket。
- 在客户端和服务端分别使用socket来获取网络通信输入/输出流,并按照一定的通信协议对socket进行读/写操作。
- 通信完成后,在客户端和服务端中分别关闭socket。
3.1.单向通信
功能:客户端发送一句话到服务器:
- 服务端代码:
public class TestServer {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定端口号
ServerSocket serverSocket = new ServerSocket(9999);
//2.等待客户端发来的信息
//accept()返回值为一个Socket,这个Socket其实就是客户端的Socket
//接到这个Socket以后,客户端和服务器才真正产生了连接,才真正可以通信了
Socket accept = serverSocket.accept();//阻塞方法:等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行。
//操作流
InputStream inputStream = accept.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);// 数据输入流
//4.读取客户端发来的数据:
String s = dataInputStream.readUTF();
//5.输出客户端信息
System.out.println("客户端发送消息:"+s);
//6.关闭流操作
dataInputStream.close();
inputStream.close();
accept.close();
serverSocket.close();
}
}
- 客户端代码
public class TestClient {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的ip和端口号:
Socket socket = new Socket("192.168.200.114",9999);
//2.利用输出流。发送数据
// 利用这个OutputStream就可以向外发送数据了,但是没有直接发送String的方法
// 所以在OutputStream外面套了一个处理流:DataOutputStream
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);//数据输出流
dataOutputStream.writeUTF("你好");
//3.关闭流和网络资源
dataOutputStream.close();
outputStream.close();
socket.close();
}
}
注意,测试的时候,先启动服务端,在启动客户端,执行后如下:
3.2.双向通信
修改为通信双方均可进行消息的处理
服务端:
public class TestServer {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定端口号
ServerSocket serverSocket = new ServerSocket(9999);
//2.等待客户端发来的信息
//accept()返回值为一个Socket,这个Socket其实就是客户端的Socket
//接到这个Socket以后,客户端和服务器才真正产生了连接,才真正可以通信了
Socket accept = serverSocket.accept();//阻塞方法:等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行。
//操作流
InputStream inputStream = accept.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);// 数据输入流
//4.读取客户端发来的数据:
String s = dataInputStream.readUTF();
//5.输出客户端信息
System.out.println("接受到客户端发送消息:"+s);
//6.向客户端发送消息
OutputStream outputStream = accept.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF("你好,很高兴认识你,我是TestServer");
//6.关闭流操作
dataOutputStream.close();
outputStream.close();
dataInputStream.close();
inputStream.close();
accept.close();
serverSocket.close();
}
}
客户端
public class TestClient {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的ip和端口号:
Socket socket = new Socket("192.168.200.114",9999);
//2.利用输出流。发送数据
// 利用这个OutputStream就可以向外发送数据了,但是没有直接发送String的方法
// 所以在OutputStream外面套了一个处理流:DataOutputStream
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);//数据输出流
dataOutputStream.writeUTF("你好");
//3.接收从服务端 发送的消息
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
System.out.println("接收到服务端发送的数据:"+dataInputStream.readUTF());
//4.关闭流和网络资源
dataOutputStream.close();
outputStream.close();
socket.close();
}
}
执行后结果如下:
3.3.对象流传送
- User实体类封装对象,传递用户名和密码
public class User implements Serializable {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
- 服务端:
public class TestServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建套接字:指定端口号
ServerSocket serverSocket = new ServerSocket(9999);
//2.等待客户端发来的信息
//accept()返回值为一个Socket,这个Socket其实就是客户端的Socket
//接到这个Socket以后,客户端和服务器才真正产生了连接,才真正可以通信了
Socket accept = serverSocket.accept();//阻塞方法:等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行。
//操作流
InputStream inputStream = accept.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);// 数据输入流
//4.读取客户端发来的数据:
User user = (User) objectInputStream.readObject();
//验证
boolean flae = false;
if(user.getUsername().equals("admin")&& user.getPassword().equals("123321")){
flae = true;
}
//5.输出客户端信息
System.out.println("接受到客户端发送消息:"+user.toString());
//6.向客户端发送消息
OutputStream outputStream = accept.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
//向客户端发送数据
dataOutputStream.writeBoolean(flae);
//6.关闭流操作
dataOutputStream.close();
outputStream.close();
objectInputStream.close();
inputStream.close();
accept.close();
serverSocket.close();
}
}
- 客户端
public class TestClient {
public static void main(String[] args) throws IOException {
//1.创建套接字:指定服务器的ip和端口号:
Socket socket = new Socket("192.168.200.114",9999);
//2.从键盘录入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = scanner.next();
System.out.print("请输入密码:");
String pwd = scanner.next();
//封装User对象传递
User user = new User(name,pwd);
//2.利用输出流。发送数据
// 利用这个OutputStream就可以向外发送数据了,但是没有直接发送String的方法
// 所以在OutputStream外面套了一个处理流:DataOutputStream
OutputStream outputStream = socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);//数据输出流
//发送对象
objectOutputStream.writeObject(user);
//3.接收从服务端 发送的消息
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
//接受发送过来的boolean类型的数据
boolean b = dataInputStream.readBoolean();
if (b){
System.out.println("恭喜,登陆成功");
}else {
System.out.println("用户名或者密码错误");
}
//4.关闭流和网络资源
dataInputStream.close();
inputStream.close();
objectOutputStream.close();
outputStream.close();
socket.close();
}
}
执行后结果如下:
3.4.加入完成处理异常方式
加入异常处理,保证代码的健壮性
- 服务端
public class TestServer {
public static void main(String[] args){
//
ServerSocket serverSocket=null;
Socket accept=null;
InputStream inputStream=null;
ObjectInputStream objectInputStream=null;
OutputStream outputStream=null;
DataOutputStream dataOutputStream=null;
try {
//1.创建套接字:指定端口号
serverSocket = new ServerSocket(9999);
//2.等待客户端发来的信息
//accept()返回值为一个Socket,这个Socket其实就是客户端的Socket
//接到这个Socket以后,客户端和服务器才真正产生了连接,才真正可以通信了
accept = serverSocket.accept();//阻塞方法:等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行。
//操作流
inputStream = accept.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);// 数据输入流
//4.读取客户端发来的数据:
User user = (User) objectInputStream.readObject();
//验证
boolean flae = false;
if(user.getUsername().equals("admin")&& user.getPassword().equals("123321")){
flae = true;
}
//5.输出客户端信息
System.out.println("接受到客户端发送消息:"+user.toString());
//6.向客户端发送消息
outputStream = accept.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
//向客户端发送数据
dataOutputStream.writeBoolean(flae);
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}finally {
//6.关闭流操作
try {
//不为null在关闭
if(dataOutputStream != null){
dataOutputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(outputStream != null){
outputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(objectInputStream != null){
objectInputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(inputStream != null){
inputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(accept != null){
accept.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
if(serverSocket != null){
serverSocket.close();
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
- 客户端
public class TestClient {
public static void main(String[] args) {
//创建
Socket socket= null;
OutputStream outputStream= null;
ObjectOutputStream objectOutputStream= null;
InputStream inputStream= null;
DataInputStream dataInputStream= null;
try {
//1.创建套接字:指定服务器的ip和端口号:
socket = new Socket("192.168.200.114",9999);
//2.从键盘录入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = scanner.next();
System.out.print("请输入密码:");
String pwd = scanner.next();
//封装User对象传递
User user = new User(name,pwd);
//2.利用输出流。发送数据
// 利用这个OutputStream就可以向外发送数据了,但是没有直接发送String的方法
// 所以在OutputStream外面套了一个处理流:DataOutputStream
outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);//数据输出流
//发送对象
objectOutputStream.writeObject(user);
//3.接收从服务端 发送的消息
inputStream = socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
//接受发送过来的boolean类型的数据
boolean b = dataInputStream.readBoolean();
if (b){
System.out.println("恭喜,登陆成功");
}else {
System.out.println("用户名或者密码错误");
}
}catch (IOException ioException){
ioException.printStackTrace();
}finally {
//关闭流
try {
if(dataInputStream != null){
dataInputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
if(inputStream != null){
inputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
if(objectOutputStream != null){
objectOutputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
if(outputStream != null){
outputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
if(socket != null){
socket.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
}
}
}
3.5.多线程接受用户请求
服务器针对一个请求服务,之后服务器就关闭了(程序自然结束了)现在需要解决:服务器必须一直在监听 ,一直开着,等待客户端的请求,这里对于服务端进行多线程改造
- 多线程类:ServerThread
public class ServerThread extends Thread{
ServerSocket serverSocket=null;
Socket accept=null;
InputStream inputStream=null;
ObjectInputStream objectInputStream=null;
OutputStream outputStream=null;
DataOutputStream dataOutputStream=null;
public ServerThread(Socket accept) {
this.accept = accept;
}
@Override
public void run() {
try{
//操作流
inputStream = accept.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);// 数据输入流
//4.读取客户端发来的数据:
User user = (User) objectInputStream.readObject();
//验证
boolean flae = false;
if(user.getUsername().equals("admin")&& user.getPassword().equals("123321")){
flae = true;
}
//5.输出客户端信息
System.out.println("接受到客户端发送消息:"+user.toString());
//6.向客户端发送消息
outputStream = accept.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
//向客户端发送数据
dataOutputStream.writeBoolean(flae);
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}finally {
//6.关闭流操作
try {
//不为null在关闭
if(dataOutputStream != null){
dataOutputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(outputStream != null){
outputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(objectInputStream != null){
objectInputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(inputStream != null){
inputStream.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
//不为null在关闭
if(accept != null){
accept.close();
}
}catch (IOException ioException){
ioException.printStackTrace();
}
try {
if(serverSocket != null){
serverSocket.close();
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
服务端
public class TestServer {
public static void main(String[] args){
System.out.println("=================服务端已启动===============");
//1.创建套接字:指定服务器的端口号
ServerSocket serverSocket = null;
Socket accept = null;
//用于记数,确定客户端的连接次数
int count = 0;
try {
//创建套接字对象:指定端口号
serverSocket = new ServerSocket(9999);
while (true){
//加入死循环,让服务器一直处于监听状态
//阻塞方法:等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行。
accept = serverSocket.accept();
count++;
//输入请求的客户端的信息:
System.out.println("当前是第"+count+"个用户访问我们的服务器,对应的用户是:"+accept.getInetAddress());
//每次过来的客户端的请求靠线程处理:
new ServerThread(accept).start();//创建启动线程
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
执行后结果如下:
四、基于UDP的网络编程
TCP(客户端和服务器端地位不平等):
- 客户端:Socket 程序感受到的 使用流 :输出流
- 服务器端: ServerSocket --->Socket 程序感受到的 使用流 :输入流
UDP(发送方和接收方的地址是平等的):
- DatagramSocket:用于发送或接收数据包的套接字
- DatagramPacket:数据包
- UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象
- 基于UDP协议的通信双方而言,没有客户端和服务器概念
UDP案例: 完成网站的咨询聊天
4.1.单向通信
- 发送方
public class TestSend {//发送方
public static void main(String[] args) throws Exception {
System.out.println("发送方 学生上线:");
//1.准备套接字:指定发送方的端口号
DatagramSocket ds = new DatagramSocket(8888);
//2.准备数据包
String str1 = "你好呀";
//转换为byte类型
byte[] bytes = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"),9999);
//发送
ds.send(dp);
//关闭资源
ds.close();
}
}
- 接受方
public class TestReceive {
public static void main(String[] args) throws IOException {
System.out.println("接收方 老师即将上线:");
//1.创建套接字:指定接受方的端口
DatagramSocket ds = new DatagramSocket(9999);
//2.创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3.接收对方的数据包,然后放入我们的dp数据包中填充
ds.receive(dp);//接收完以后 dp里面就填充好内容了
//取出数据
byte[] data = dp.getData();
//注意这里不要导错包
String s = new String(data, 0, dp.getLength());
System.out.println("学生说:"+s);
//关闭
ds.close();
}
}
4.2.双向通信
- 发送方
public class TestSend {//发送方
public static void main(String[] args) throws Exception {
System.out.println("发送方 学生上线:");
//1.准备套接字:指定发送方的端口号
DatagramSocket ds = new DatagramSocket(8888);
//2.准备数据包
Scanner scanner = new Scanner(System.in);
System.out.print("学生输入信息:");
String str1 = scanner.next();
//转换为byte类型
byte[] bytes = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"),9999);
//发送
ds.send(dp);
//接受老师发送的数据
//创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes2 = new byte[1024];
DatagramPacket dp2 = new DatagramPacket(bytes2, bytes2.length);
ds.receive(dp2);//接收完以后 dp里面就填充好内容了、
//取出数据
//注意这里不要导错包
String s = new String(dp2.getData(), 0, dp.getLength());
System.out.println("老师回复说:"+s);
//关闭资源
ds.close();
}
}
- 接受方
public class TestReceive {
public static void main(String[] args) throws IOException {
System.out.println("接收方 老师即将上线:");
//1.创建套接字:指定接受方的端口
DatagramSocket ds = new DatagramSocket(9999);
//2.创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3.接收对方的数据包,然后放入我们的dp数据包中填充
ds.receive(dp);//接收完以后 dp里面就填充好内容了
//取出数据
byte[] data = dp.getData();
//注意这里不要导错包
String s = new String(data, 0, dp.getLength());
System.out.println("学生说:"+s);
//向学生发送数据
Scanner scanner = new Scanner(System.in);
System.out.print("给学生回复信息:");
String str1 = scanner.next();
//转换为byte类型
byte[] bytes2 = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp2 = new DatagramPacket(bytes2, bytes2.length, InetAddress.getByName("localhost"),8888);
//发送
ds.send(dp2);
//关闭
ds.close();
}
}
4.3.异常处理
- 发送方
public class TestSend {//发送方
public static void main(String[] args) {
System.out.println("发送方 学生上线:");
DatagramSocket ds = null;
try {
//1.准备套接字:指定发送方的端口号
ds = new DatagramSocket(8888);
//2.准备数据包
Scanner scanner = new Scanner(System.in);
System.out.print("学生输入信息:");
String str1 = scanner.next();
//转换为byte类型
byte[] bytes = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"),9999);
//发送
ds.send(dp);
//接受老师发送的数据
//创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes2 = new byte[1024];
DatagramPacket dp2 = new DatagramPacket(bytes2, bytes2.length);
ds.receive(dp2);//接收完以后 dp里面就填充好内容了、
//取出数据
//注意这里不要导错包
String s = new String(dp2.getData(), 0, dp.getLength());
System.out.println("老师回复说:"+s);
}catch (IOException ioException){
ioException.printStackTrace();
}finally {
//关闭资源
if(ds != null){
ds.close();
}
}
}
}
- 接受方
public class TestReceive {
public static void main(String[] args) throws IOException {
System.out.println("接收方 老师即将上线:");
DatagramSocket ds = null;
try {
//1.创建套接字:指定接受方的端口
ds = new DatagramSocket(9999);
//2.创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3.接收对方的数据包,然后放入我们的dp数据包中填充
ds.receive(dp);//接收完以后 dp里面就填充好内容了
//取出数据
byte[] data = dp.getData();
//注意这里不要导错包
String s = new String(data, 0, dp.getLength());
System.out.println("学生说:"+s);
//向学生发送数据
Scanner scanner = new Scanner(System.in);
System.out.print("给学生回复信息:");
String str1 = scanner.next();
//转换为byte类型
byte[] bytes2 = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp2 = new DatagramPacket(bytes2, bytes2.length, InetAddress.getByName("localhost"),8888);
//发送
ds.send(dp2);
}finally {
if(ds != null){
//关闭
ds.close();
}
}
}
}
4.4.正常通信
- 发送方
public class TestSend {//发送方
public static void main(String[] args) {
System.out.println("发送方 学生上线:");
DatagramSocket ds = null;
try {
//1.准备套接字:指定发送方的端口号
ds = new DatagramSocket(8888);
while (true){
//2.准备数据包
Scanner scanner = new Scanner(System.in);
System.out.print("学生输入信息:");
String str1 = scanner.next();
//转换为byte类型
byte[] bytes = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"),9999);
//发送
ds.send(dp);
//接受老师发送的数据
//创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes2 = new byte[1024];
DatagramPacket dp2 = new DatagramPacket(bytes2, bytes2.length);
ds.receive(dp2);//接收完以后 dp里面就填充好内容了、
//取出数据
//注意这里不要导错包
String s = new String(dp2.getData(), 0, dp.getLength());
System.out.println("老师回复说:"+s);
}
}catch (IOException ioException){
ioException.printStackTrace();
}finally {
//关闭资源
if(ds != null){
ds.close();
}
}
}
}
- 接受方
public class TestReceive {
public static void main(String[] args) throws IOException {
System.out.println("接收方 老师即将上线:");
DatagramSocket ds = null;
try {
//1.创建套接字:指定接受方的端口
ds = new DatagramSocket(9999);
while (true){
//2.创建空的数据包,打算用来接收 对方传过来的数据包:
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3.接收对方的数据包,然后放入我们的dp数据包中填充
ds.receive(dp);//接收完以后 dp里面就填充好内容了
//取出数据
byte[] data = dp.getData();
//注意这里不要导错包
String s = new String(data, 0, dp.getLength());
System.out.println("学生说:"+s);
//向学生发送数据
Scanner scanner = new Scanner(System.in);
System.out.print("给学生回复信息:");
String str1 = scanner.next();
//转换为byte类型
byte[] bytes2 = str1.getBytes();
/*
需要四个参数:
1.指的是传送数据转为字节数组
2.字节数组的长度
3.封装接收方的ip
4.指定接收方的端口号
*/
DatagramPacket dp2 = new DatagramPacket(bytes2, bytes2.length, InetAddress.getByName("localhost"),8888);
//发送
ds.send(dp2);
}
}finally {
if(ds != null){
//关闭
ds.close();
}
}
}
}