30、JAVA进阶——Socket编程_socket编程java
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
✅作者简介热爱国学的Java后端开发者修心和技术同步精进。
🍎个人主页乐趣国学的博客
🍊个人信条不迁怒不贰过。小知识大智慧。
💞当前专栏JAVA开发者成长之路
✨特色专栏国学周更-心性养成之路
🥭本文内容JAVA进阶——Socket编程
更多内容点击👇
目录
1.DatagramPacket类和DatagramSocket类
一、Socket知识
1. Socket概述
1Java最初是作为网络编程语言出现的它对网络的高度支持使得客户端和服务器端流畅的沟通成为现实。
2在网络编程中使用最多的就是Socket每一个实用的网络程序都少不了它的参与。
3在计算机网络编程技术中两个进程或者说两台计算机可以通过一个网络通信连接实现数据的交换这种通信链路的端点就被称为“套接字”英文名称也就是Socket。
4Socket是网络驱动层提供给应用程序的一个接口或者说一种机制。
5使用物流送快递的例子来说明Socket
-->发件人将有收货人地址信息的货物送到快递站发件人不用关心物流是如何进行的货物被送到收货人所在地区的快递站点进行配送收货人等待收货就可以了。
-->这个过程很形象地说明了信息在网络中传递的过程。其中货物就是数据信息2个快递站点就是2个端点Socket。
6信息如何在网络中寻址传递应用程序并不用关心只负责准备发送数据和接收数据即可。
2. Socket通信原理
1对于编程人员来说无须了解Socket底层机制是如何传送数据的而是直接将数据提交给SocketSocket会根据应用程序提供的相关信息通过一系列计算绑定IP及信息数据将数据交给驱动程序向网络上发送。
2Socket的底层机制非常复杂Java平台提供了一些简单但是强大的类可以简单有效地使用Socket开发通信程序而无须了解底层机制。
3. java.net包
1java.net包提供了若干支持基于套接字的客户端/服务器通信的类。
2java.net包中常用的类有Socket、ServerSocket、DatagramPacket、DatagramSocket、InetAddress、URL、URLConnection和URLEncoder等。
3为了监听客户端的连接请求可以使用ServerSocket类。
4Socket类实现用于网络上进程间通信的套接字。
5DatagramSocket类使用UDP协议实现客户端和服务器套接字。
6DatagramPacket类使用DatagramSocket类的对象封装设置和收到的数据报。
7InetAddress类表示Internet地址。
8在创建数据报报文和Socket对象时可以使用InetAddress类
二、基于TCP协议的Socket编程
1.Socket类和ServerSocket类
1java.net包的两个类Socket和ServerSocket分别用来实现双向安全连接的客户端和服务器端它们是基于TCP协议进行工作的工作过程如同打电话的过程只有双方都接通了才能开始通话。
2进行网络通信时Socket需要借助数据流来完成数据的传递工作。
3一个应用程序要通过网络向另一个应用程序发送数据只要简单地创建Socket然后将数据写入到与该Socket关联的输出流即可。对应的接收方的应用程序创建Socket从相关联的输入流读取数据即可。
4注意2个端点在基于TCP协议的Socket编程中经常一个作为客户端一个作为服务器端也就是遵循client-server模型。
● Socket类
Socket对象在客户端和服务器之间建立连接。可用Socket类的构造方法创建套接字并将此套接字连接至指定的主机和端口。
1构造方法
-->第一种构造方法以主机名和端口号作为参数来创建一个Socket对象。创建对象时可能抛出UnknownHostException或IOException异常必须捕获它们。
Socket s = new Socket(hostName,port);
-->第二种构造方法以InetAddress对象和端口号作为参数来创建一个Socket对象。构造方法可能抛出IOException或UnknownHostException异常必须捕获并处理它们。
Socket s = new Socket(address,port);
2常用方法
● ServerSocket类
ServerSocket对象等待客户端建立连接连接建立以后进行通信。
1构造方法
-->第一种构造方法接受端口号作为参数创建ServerSocket对象创建此对象时可能抛出IOException异常必须捕获和处理它。
ServerSocket ss = new ServerSocket(port);
-->第二种构造方法接受端口号和最大队列长度作为参数队列长度表示系统在拒绝连接前可以拥有的最大客户端连接数。
ServerSocket ss = new ServerSocket(port,maxqu);
2常用方法
-->Socket类中列出的方法也适用于ServerSocket类。
-->ServerSocket类具有accept()方法此方法用于等待客户端发起通信这样Socket对象就可用于进一步的数据传输。
2.使用Socket编程实现登录功能
● 实现单用户登录
-->Socket网络编程一般分成如下4个步骤进行
1建立连接。
2打开Socket关联的输入/输出流。
3从数据流中写入信息和读取信息。
4关闭所有的数据流和Socket。
-->使用两个类模拟实现用户登录的功能实现客户端向服务器端发送用户登录信息服务器端显示这些信息。
客户端实现步骤
1建立连接连接指向服务器及端口。
2打开Socket关联的输入/输出流。
3向输出流中写入信息。
4从输入流中读取响应信息。
5关闭所有的数据流和Socket。
服务器端实现步骤
1建立连接监听端口。
2使用accept()方法等待客户端发起通信
3打开Socket关联的输入/输出流。
4从输入流中读取请求信息。
5向输出流中写入信息。
6关闭所有的数据流和Socket。
-->客户端和服务器端的交互采用一问一答的模式先启动服务器进入监听状态等待客户端的连接请求连接成功以后客户端先“发言”服务器给予“回应”。
示例01实现传递对象信息。
♥ user类
package cn.bdqn.demo02; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; /** 用户名 */ private String loginName; /** 用户密码 */ private String pwd; public User() { super(); } public User(String loginName, String pwd) { super(); this.loginName = loginName; this.pwd = pwd; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
♥ LoginServer类
package cn.bdqn.demo02; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ObjectInputStream ois = null; OutputStream os = null; try { // 建立一个服务器SocketServerSocket指定端口8800并开始监听 serverSocket = new ServerSocket(8800); // 使用accept()方法等待客户端发起通信 socket = serverSocket.accept(); // 打开输入流 is = socket.getInputStream(); // 反序列化 ois = new ObjectInputStream(is); // 获取客户端信息即从输入流读取信息 User user = (User) ois.readObject(); if (user != null) { System.out.println("我是服务器客户登录信息为" + user.getLoginName() + "" + user.getPwd()); } // 给客户端一个响应即向输出流中写入信息 String reply = "欢迎你登录成功"; os = socket.getOutputStream(); os.write(reply.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { // 关闭资源 try { os.close(); ois.close(); is.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
♥ LoginClient类
package cn.bdqn.demo02; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class LoginClient { /* * 示例02升级演示示例01实现传递对象信息。 */ public static void main(String[] args) { Socket socket = null; OutputStream os = null; ObjectOutputStream oos = null; InputStream is = null; BufferedReader br = null; try { // 建立客户端Socket连接指定服务器的位置为本机以及端口为8800 socket = new Socket("localhost", 8800); // 打开输出流 os = socket.getOutputStream(); // 对象序列化 oos = new ObjectOutputStream(os); // 发送客户端信息即向输出流中写入信息 User user = new User("Tom", "123456"); oos.writeObject(user); socket.shutdownOutput(); // 接收服务器端的响应即从输入流中读取信息 is = socket.getInputStream(); br = new BufferedReader(new InputStreamReader(is)); String reply; while ((reply = br.readLine()) != null) { System.out.println("我是客户端服务器的响应为" + reply); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
示例02升级演示示例01实现传递多个对象信息。
♥ user类
package cn.bdqn.demo03; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; /** 用户名 */ private String loginName; /** 用户密码 */ private String pwd; public User() { super(); } public User(String loginName, String pwd) { super(); this.loginName = loginName; this.pwd = pwd; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
♥ LoginServer类
package cn.bdqn.demo03; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ObjectInputStream ois = null; OutputStream os = null; try { // 建立一个服务器SocketServerSocket指定端口8800并开始监听 serverSocket = new ServerSocket(8800); // 使用accept()方法等待客户端发起通信 socket = serverSocket.accept(); // 打开输入流 is = socket.getInputStream(); // 反序列化 ois = new ObjectInputStream(is); // 获取客户端信息即从输入流读取信息 User[] users = (User[]) ois.readObject(); for (int i = 0; i < users.length; i++) { System.out.println("我是服务器客户登录信息为" + users[i].getLoginName() + "" + users[i].getPwd()); } // 给客户端一个响应即向输出流中写入信息 String reply = "欢迎你登录成功"; os = socket.getOutputStream(); os.write(reply.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { // 关闭资源 try { os.close(); ois.close(); is.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
♥ LoginClient类
package cn.bdqn.demo03; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class LoginClient { /* * 示例02升级演示示例01实现传递对象信息。 */ public static void main(String[] args) { Socket socket = null; OutputStream os = null; ObjectOutputStream oos = null; InputStream is = null; BufferedReader br = null; try { // 建立客户端Socket连接指定服务器的位置为本机以及端口为8800 socket = new Socket("localhost", 8800); // 打开输出流 os = socket.getOutputStream(); // 对象序列化 oos = new ObjectOutputStream(os); // 发送客户端信息即向输出流中写入信息 User user1 = new User("Tom", "123456"); User user2 = new User("bob", "123456"); User user3 = new User("lisa", "123456"); User[] users = {user1,user2,user3}; oos.writeObject(users); socket.shutdownOutput(); // 接收服务器端的响应即从输入流中读取信息 is = socket.getInputStream(); br = new BufferedReader(new InputStreamReader(is)); String reply; while ((reply = br.readLine()) != null) { System.out.println("我是客户端服务器的响应为" + reply); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
● 实现多客户端用户登录
-->一问一答的模式在现实中显然不是人们想要的。一个服务器不可能只针对一个客户端服务一般是面向很多的客户端同时提供服务的但是单线程实现必然是这样的结果。
-->解决这个问题的办法是采用多线程的方式可以在服务器端创建一个专门负责监听的应用主服务程序、一个专门负责响应的线程程序。这样可以利用多线程处理多个请求。
->客户端实现步骤
1建立连接连接指向服务器及端口。
2打开Socket关联的输入/输出流。
3向输出流中写入信息。
4从输入流中读取响应信息。
5关闭所有的数据流和Socket。
-->服务器端实现步骤
1创建服务器线程类run()方法中实现对一个请求的响应处理。
2修改服务器端代码让服务器端Socket一直处于监听状态。
3服务器端每监听到一个请求创建一个线程对象并启动。
示例03升级演示示例02实现多客户端的响应处理。
♥ user类
package cn.bdqn.demo04; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; /** 用户名 */ private String loginName; /** 用户密码 */ private String pwd; public User() { super(); } public User(String loginName, String pwd) { super(); this.loginName = loginName; this.pwd = pwd; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
♥ LoginThread
package cn.bdqn.demo04; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.Socket; public class LoginThread extends Thread { /* * 示例03升级示例02实现多客户端的响应处理。 * * 分析如下 * 1创建服务器端线程类run()方法中实现对一个请求的响应处理。 * 2修改服务器端代码让服务器端Socket一直处于监听状态。 * 3服务器端每监听到一个请求创建一个线程对象并启动 */ Socket socket = null; //每启动一个线程连接对应的Socket public LoginThread(Socket socket) { this.socket = socket; } //启动线程即响应客户请求 public void run() { InputStream is = null; ObjectInputStream ois = null; OutputStream os = null; try { //打开输入流 is = socket.getInputStream(); //反序列化 ois = new ObjectInputStream(is); //获取客户端信息即从输入流读取信息 User user = (User)ois.readObject(); if(user!=null){ System.out.println("我是服务器客户登录信息为"+user.getLoginName()+""+user.getPwd()); } //给客户端一个响应即向输出流中写入信息 os = socket.getOutputStream(); String reply = "欢迎你登录成功"; os.write(reply.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ try { os.close(); ois.close(); is.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
♥ LoginServer类
package cn.bdqn.demo04; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) { ServerSocket serverSocket = null; try { // 建立一个服务器Socket(ServerSocket)指定端口并开始监听 serverSocket = new ServerSocket(8800); // 监听一直进行中 while (true) { // 使用accept()方法等待客户发起通信 Socket socket = serverSocket.accept(); LoginThread loginThread = new LoginThread(socket); loginThread.start(); } } catch (IOException e) { e.printStackTrace(); } } }
♥ LoginClient1类
package cn.bdqn.demo04; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class LoginClient01 { /* * 客户端通过输出流向服务器端发送请求信息 * 服务器侦听客户端的请求得到一个Socket对象将这个Socket对象传递给线程类 * 线程类通过输入流获取客户端的请求并通过输出流向客户端发送响应信息 * 客户端通过输入流读取服务器发送的响应信息 * */ /* * 示例03升级演示示例02实现多客户端的响应处理 */ public static void main(String[] args) { Socket socket = null; OutputStream os = null; ObjectOutputStream oos = null; InputStream is = null; BufferedReader br = null; try { // 建立客户端Socket连接指定服务器的位置为本机以及端口为8800 socket = new Socket("localhost", 8800); // 打开输出流 os = socket.getOutputStream(); // 对象序列化 oos = new ObjectOutputStream(os); // 发送客户端信息即向输出流中写入信息 User user = new User("Tom", "123456"); oos.writeObject(user); socket.shutdownOutput(); // 接收服务器端的响应即从输入流中读取信息 is = socket.getInputStream(); br = new BufferedReader(new InputStreamReader(is)); String reply; while ((reply = br.readLine()) != null) { System.out.println("我是客户端服务器的响应为" + reply); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
♥ LoginClient2类和LoginClient3类
同LoginClient1类一样创建不同的User对象即可
-->java.net包中的InetAddress类用于封装IP地址和DNS。要创建InetAddress类的实例可以使用工厂方法因为此类没有构造方法。
-->InetAddress类中的工厂方法
-->如果找不到主机两种方法都将抛出UnknownHostNameException异常。
三、基于UDP协议的Socket编程
TCP | UDP | |
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
1.DatagramPacket类和DatagramSocket类
1基于TCP的网络通信是安全的是双向的如同打电话需要先有服务端建立双向连接后才开始数据通信。
2基于UDP的网络通信只需要指明对方地址然后将数据送出去并不会事先连接。这样的网络通信是不安全的所以只应用在如聊天系统、咨询系统等场合下。
3数据报是表示通信的一种报文类型使用数据报进行通信时无须事先建立连接它是基于UDP协议进行的。
4Java中有两个可使用数据报实现通信的类即DatagramPacket和DatagramSocket。
5DatagramPacket类起到容器的作用DatagramSocket类用于发送或接收DatagramPacket。
6DatagramPacket类不提供发送或接收数据的方法而DatagramSocket类提供send()方法和receive()方法用于通过套接字发送和接收数据报。
● DatagramPacket类
1构造方法
-->客户端要向外发送数据必须首先创建一个DatagramPacket对象再使用DatagramSocket对象发送。
2常用方法
● DatagramSocket类
1构造方法
-->DatagramSocket类不维护连接状态不产生输入/输出数据流它的唯一作用就是接收和发送DatagramPacket对象封装好的数据报。
2常用方法
2.使用Socket编程实现客户咨询
-->利用UDP通信的两个端点是平等的也就是说通信的两个程序关系是对等的没有主次之分甚至它们的代码都可以完全是一样的这一点要与基于TCP协议的Socket编程区分开来。
-->基于UDP协议的Socket网络编程一般按照以下4个步骤进行
1利用DatagramPacket对象封装数据包。
2利用DatagramSocket对象发送数据包。
3利用DatagramSocket对象接收数据包。
4利用DatagramPacket对象处理数据包。
-->模拟客户咨询功能实现发送方发送咨询问题接收方接收并显示发送来的咨询问题。
发送方实现步骤
1获取本地主机的InetAddress对象。
2创建DatagramPacket对象封装要发送的信息。
3利用DatagramSocket对象将DatagramPacket对象数据发送出去。
接收方实现步骤
1创建DatagramPacket对象准备接收封装的数据。
2创建DatagramSocket对象接收数据保存于DatagramPacket对象中。
3利用DatagramPacket对象处理数据。
示例04发送方发送咨询问题接收方回应咨询。
♥ Receive类
package cn.bdqn.demo05; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketAddress; import java.net.SocketException; public class Receive { public static void main(String[] args) { /* * 示例06发送方发送咨询问题接收方回应咨询。 * * 接收方实现步骤如下 * 1创建DatagramPacket对象准备接收封装的数据。 * 2创建DatagramSocket对象接收数据保存于DatagramPacket对象中。 * 3利用DatagramPacket对象处理数据。 */ DatagramSocket ds = null; DatagramPacket dp = null; DatagramPacket dpto = null; // 创建DatagramPacket对象用来准备接收数据 byte[] buf = new byte[1024]; dp = new DatagramPacket(buf, 1024); try { // 创建DatagramSocket对象接收数据 ds = new DatagramSocket(8800); ds.receive(dp); // 显示接收到的信息 String mess = new String(dp.getData(), 0, dp.getLength()); System.out.println(dp.getAddress().getHostAddress() + "说" + mess); String reply = "你好我在请咨询"; // 显示与本地对话框 System.out.println("我 说" + reply); // 创建DatagramPacket对象封装数据 SocketAddress sa = dp.getSocketAddress(); dpto = new DatagramPacket(reply.getBytes(), reply.getBytes().length, sa); ds.send(dpto); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { ds.close(); } } }
♥ Send类
package cn.bdqn.demo05; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; public class Send { /* * 示例06升级示例05发送方发送咨询问题接收方回应咨询。 * * 发送方实现步骤如下 * 1获取本地主机的InetAddress对象。 * 2创建DatagramPacket对象封装要发送的信息。 * 3利用DatagramSocket对象将DatagramPacket对象数据发送出去。 */ public static void main(String[] args) { DatagramSocket ds = null; InetAddress ia = null; String mess = "你好我想咨询一个问题。"; System.out.println("我说" + mess); try { // 获取本地主机地址 ia = InetAddress.getByName("localhost"); // 创建DatagramPacket对象封装数据 DatagramPacket dp = new DatagramPacket(mess.getBytes(), mess.getBytes().length, ia, 8800); // 创建DatagramSocket对象向服务器发送数据 ds = new DatagramSocket(); ds.send(dp); byte[] buf = new byte[1024]; DatagramPacket dpre = new DatagramPacket(buf, buf.length); ds.receive(dpre); // 显示接收到的信息 String reply = new String(dpre.getData(), 0, dpre.getLength()); System.out.println(dpre.getAddress().getHostAddress() + "说" + reply); } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { ds.close(); } } }