Java 网络编程
网络编程
- 一、了解网络
- 网络的概念
- 二、网络编程的三要素
- 网络模型
- OSI参考模型
- TCP/IP参考模型
- IP地址
- IP地址的表示方法
- IP地址的分类
- 特殊的IP地址
- IP地址所对应的对象(InetAddress)
- 端口
- 传输协议
- 三、Socket套接字
- 基于TCP协议的Socket编程
- Socket中实现对象的传递
- 基于UDP协议的Socket编程
一、了解网络
网络的概念
- 网络:一组相互连接的计算机
1、多台计算机组成。
2、使用物理线路进行连接。
二、网络编程的三要素
- IP地址:唯一标识网络上的每台计算机;是两台计算机之间通信的必备要素。
- 端口号:计算机中应用的标号:代表一个应用程序);0-1024系统使用或保留端口,有效端口0-65536.
- 通信协议:通信的规则,包含TCP和UDP。
- 网络中的每台计算机都有一个唯一的IP地址,网络通过IP地址进行通信。
- 计算机A使用QQ与外界交流,那么计算机B的使用者只能通过QQ去解析对方发来的信息,计算机中有那么多的应用程序,使用数字来对这些网络应用程序做标识,这些数字形象的称之为端口号。
- 通信之间必须使用相同的规则,这些规则叫协议,国际通用协议TCP/UDP。
网络模型
OSI参考模型
开放系统互连参考模型(Open System Interconnect)
TCP/IP参考模型
传输控制/国际协议(Transfer Controln Protocol/Internet Protocol)
IP地址
IP地址的表示方法
IP地址:32位,由4个8位二进制数组成。
IP表示方法:点分十进制。
IP地址=网络ID+主机ID
- 网络ID:标识计算机或网络设备所在的网段。
- 主机ID:标识特定主机或网络设备。
IP地址的分类
地址类用于指定网络ID并在网络ID和主机ID之间提供分隔方法。
IANA负责分配A、B、C类网络地址,具体主机地址由机构组织自行分配。
IP地址类分类:
- A类:一个A类IP地址由1字节的网络地址和3字节主机地址组成,它主要为大型网络而设计的,网络地址的最高位必须是“0”, 地址范围从1.0.0.0 到127.0.0.0)。可用的A类网络有127个,每个网络能容纳16777214个主机。
- B类:一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,地址范围从128.0.0.0到191.255.255.255。可用的B类网络有16382个,每个网络能容纳6万多个主机 。
- C类:一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”。范围从192.0.0.0到223.255.255.255。C类网络可达209万余个,每个网络能容纳254个主机。
- D类: D类IP地址是一个专门保留的地址。它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中。多点广播地址用来一次寻址一组计算机,它标识共享同一协议的一组计算机。224.0.0.0到239.255.255.255用于多点广播 。
- E类:以“llll0”开始,为将来使用保留。240.0.0.0到255.255.255.254,255.255.255.255用于广播地址。
注:在IP地址3种主要类型里,各保留了3个区域作为私有地址,其地址范围如下:
A类地址:10.0.0.0~10.255.255.255
B类地址:172.16.0.0~172.31.255.255
C类地址:192.168.0.0~192.168.255.255
特殊的IP地址
0.0.0.0:本机。
127.0.0.1:本机回环地址,用于本机测试。
255.255.255.255:当前子网,一般用于向当前子网广播信息。
IP地址所对应的对象(InetAddress)
方法 描述 public static InetAddress getLocalhost( ) 获取主机名和IP地址 public String getHostAddress( ) 获取IP地址 public String getHostName( ) 获取主机名 public static InetAddress getByName(String host) 根据主机名获得IP地址 示例代码:
public class InteAddressDemo { public static void main(String[] args) throws UnknownHostException { InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost); InetAddress inetAdd = InetAddress.getByName("www.baidu.com"); System.out.println(inetAdd); System.out.println(inetAdd.getHostAddress()); System.out.println(inetAdd.getHostName()); } } /* 输出的结果是: // 此行会输出你的主机名和IP地址我就不漏出我的主机命和IP地址了 www.baidu.com/183.2.172.42 183.2.172.42 www.baidu.com */
端口
端口:port
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。
传输协议
- UDP:相当于发短信(有字数限制),不需要建立连接,数据报的大小在64k内,效率较高,但不安全,容易丢包。
- TCP:相当于打电话,需要建立连接,效率相对比较低,数据传输安全,三次握手完成。(点名–》答到–》确认)
三、Socket套接字
- 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。
- Java中使用Socket完成TCP程序的开发,使用此类可以方便的建立可靠的、双向的、持续性的、点对点的通讯连接。
- 在Socket的程序开发中,服务器端使用ServerSocket等待客户端的连接,对于Java的网络程序来讲,每个客户端都使用一个Socket对象表示。
基于TCP协议的Socket编程
进行网络通信时,Socket需要借助数据流来完成数据的传递工作。
演示案例: 互相传输一句话。
服务器端:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { //创建serverSocket对象 ServerSocket serverSocket = new ServerSocket(10000); //获取服务端的套接字对象 Socket socket = serverSocket.accept(); //--------------------接收客户端的输入--------------- //获取输入流对象 InputStream inputStream = socket.getInputStream(); byte[] buf = new byte[1024]; int length = inputStream.read(buf); System.out.println("客户端传输的数据是:" + new String(buf,0,length)); //------------------返回客户端数据------------------ OutputStream outputStream = socket.getOutputStream(); outputStream.write("你好,收到".getBytes()); //关闭流操作 outputStream.close(); inputStream.close(); socket.close(); serverSocket.close(); } } // 输出的结果是: // 客户端传输的数据是:hello java
客户端:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class Client { public static void main(String[] args) throws IOException { //创建客户端的套接字 Socket client = new Socket("127.0.0.1",10000); //------------------------向外进行输出------------------ //获取输出流对象 OutputStream outputStream = client.getOutputStream(); //数据输出 outputStream.write("hello java".getBytes()); //------------------------接收服务端返回的消息------------- //获取输入流对象 InputStream inputStream = client.getInputStream(); byte[] buf = new byte[1024]; int length = inputStream.read(buf); System.out.println("服务端的响应数据是:" + new String(buf,0,length)); //关闭流操作 inputStream.close(); outputStream.close(); client.close(); } } // 输出的结果是: // 服务端的响应数据是:你好,收到
演示案例: 客户端向服务器端提交一张照片。
服务器端:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class PicServer { public static void main(String[] args) throws IOException { //创建服务端对象,开放端口 ServerSocket serverSocket = new ServerSocket(10086); //创建服务器的Socket Socket server = serverSocket.accept(); //获取输入流对象 InputStream inputStream = server.getInputStream(); //创建文件输出流对象 FileOutputStream fileOutputStream = new FileOutputStream("2.jpg"); int temp = 0; while ((temp = inputStream.read()) != -1){ fileOutputStream.write(temp); } //添加流输出完成的标志 server.shutdownInput(); //上传图片结束之后给予客户端响应 OutputStream outputStream = server.getOutputStream(); outputStream.write("上传成功".getBytes()); server.shutdownOutput(); //关闭流 outputStream.close(); fileOutputStream.close(); inputStream.close(); server.close(); serverSocket.close(); } }
客户端:
import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class PicClient { public static void main(String[] args) throws Exception { //创建图片的输入流对象 FileInputStream fileInputStream = new FileInputStream("1.jpg"); //创建Socket Socket client = new Socket("localhost",10086); //获取输出流对象 OutputStream outputStream = client.getOutputStream(); int temp = 0; while ((temp = fileInputStream.read()) != -1){ outputStream.write(temp); } client.shutdownOutput(); //接收服务端的响应 InputStream inputStream = client.getInputStream(); byte[] buf = new byte[1024]; int length = inputStream.read(buf); System.out.println(new String(buf,0,length)); client.shutdownInput(); //关闭流操作 inputStream.close(); outputStream.close(); client.close(); fileInputStream.close(); } }
注意: 要先启动服务器端再启动客户端。
Socket中实现对象的传递
如何传递对象信息呢?
String info = "用户名:Lisi;用户密码:123456"; outputStream.write(info.getBytes());
序列化
User user = new User();// User是用户类 user.setLoginName("Lisi"); user.setPassword("123456"); oos.writeObject(user);
演示案例: 实现用户登录。
- 方法一
用户类:
import java.io.Serial; import java.io.Serializable; public class User implements Serializable { @Serial// 指定序列化版本号 private static final long serialVersionUID = -8254223107980283532L; private String username; private String password; public User() { } 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; } }
服务器端:
import java.io.DataOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.net.ServerSocket; import java.net.Socket; public class LoginServer { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(10000); while (true){ Socket server = serverSocket.accept(); //获取输入流对象 InputStream inputStream = server.getInputStream(); //需要使用ObjectInputStream对象 ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); User user = (User) objectInputStream.readObject(); String str = ""; if ("lyf".equals(user.getUsername()) && "lyf".equals(user.getPassword())){ str = "登录成功"; System.out.println("欢迎您:" + user.getUsername()); } else { str = "登录失败"; } //截断输入流 server.shutdownInput(); //给客户端响应 DataOutputStream dataOutputStream = new DataOutputStream(server.getOutputStream()); dataOutputStream.writeUTF(str); server.shutdownOutput(); //关闭流操作 dataOutputStream.close(); objectInputStream.close(); inputStream.close(); server.close(); } } } // 输出的结果是: // 欢迎您:lyf
客户端:
import java.io.DataInputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class LoginClient { public static void main(String[] args) throws IOException { Socket client = new Socket("localhost",10000); OutputStream outputStream = client.getOutputStream(); //完成用户登录功能,需要传输一个user对象 User user = getUser(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(user); //调用shutdown方法告诉对方传输完成 client.shutdownOutput(); //接收响应 DataInputStream dataInputStream = new DataInputStream(client.getInputStream()); String str = dataInputStream.readUTF(); System.out.println(str); //关闭流操作 dataInputStream.close(); objectOutputStream.close(); outputStream.close(); client.close(); } public static User getUser(){ Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String username = scanner.nextLine(); System.out.println("请输入密码:"); String password = scanner.nextLine(); return new User(username,password); } } /* 请输入用户名: lyf 请输入密码: lyf 输入正确的用户名和密码后会返回:登录成功 */
- 方法二
用户类:
import java.io.Serial; import java.io.Serializable; public class User implements Serializable { @Serial// 指定序列化版本号 private static final long serialVersionUID = -8254223107980283532L; private String username; private String password; public User() { } 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; } }
服务器端:
import java.net.ServerSocket; import java.net.Socket; public class LoginServer2 { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(10000); while (true){ Socket socket = serverSocket.accept(); LoginThread loginThread = new LoginThread(socket); new Thread(loginThread).start(); } } } // 输出的结果是: // 欢迎您:lyf
线程类:
import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket; public class LoginThread implements Runnable{ private Socket socket; public LoginThread(Socket socket) { this.socket = socket; } @Override public void run() { ObjectInputStream objectInputStream = null; DataOutputStream dataOutputStream = null; try { objectInputStream = new ObjectInputStream(socket.getInputStream()); User user = (User) objectInputStream.readObject(); String str = ""; if ("lyf".equals(user.getUsername()) && "lyf".equals(user.getPassword())){ str = "登录成功"; System.out.println("欢迎您:" + user.getUsername()); } else { str = "登录失败"; } socket.shutdownInput(); dataOutputStream = new DataOutputStream(socket.getOutputStream()); dataOutputStream.writeUTF(str); socket.shutdownOutput(); } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); }finally { try { dataOutputStream.close(); objectInputStream.close(); socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
客户端:
import java.io.DataInputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class LoginClient { public static void main(String[] args) throws IOException { Socket client = new Socket("localhost",10000); OutputStream outputStream = client.getOutputStream(); //完成用户登录功能,需要传输一个user对象 User user = getUser(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(user); //调用shutdown方法告诉对方传输完成 client.shutdownOutput(); //接收响应 DataInputStream dataInputStream = new DataInputStream(client.getInputStream()); String str = dataInputStream.readUTF(); System.out.println(str); //关闭流操作 dataInputStream.close(); objectOutputStream.close(); outputStream.close(); client.close(); } public static User getUser(){ Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String username = scanner.nextLine(); System.out.println("请输入密码:"); String password = scanner.nextLine(); return new User(username,password); } } /* 请输入用户名: lyf 请输入密码: lyf 输入正确的用户名和密码后会返回:登录成功 */
上述两种方法都能实现客户端与服务器端之间的用户登录效果。
基于UDP协议的Socket编程
TCP和UDP之间的区别:
1、TCP协议通信双方需要建立连接,而UDP协议通信双方不需要建立连接。
2、TCP协议通信双方连接建立时双方存在主次之分,而UDP协议通信双方完全平等。
示例代码:
服务器端:
import java.net.DatagramPacket; import java.net.DatagramSocket; public class UDPServer { public static void main(String[] args) throws Exception { DatagramSocket datagramSocket = new DatagramSocket(10001); byte[] buf = new byte[1024]; //用来接收传输过来的数据 DatagramPacket datagramPacket = new DatagramPacket(buf,buf.length); //利用创建好的数据报包对象来接收数据 datagramSocket.receive(datagramPacket); //打印输出信息 System.out.println(new String(datagramPacket.getData(),0,datagramPacket.getLength())); datagramSocket.close(); } } // 接收客户端控制台上传输过来的数据并打印
客户端:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; public class UDPClient { public static void main(String[] args) throws Exception { //创建udp通信的socket DatagramSocket datagramSocket = new DatagramSocket(10000); //从控制台读取数据 Scanner scanner = new Scanner(System.in); String str = scanner.nextLine(); DatagramPacket datagramPacket = new DatagramPacket(str.getBytes(),str.getBytes().length, InetAddress.getByName("localhost"),10001); datagramSocket.send(datagramPacket); datagramSocket.close(); } } // 在客户端的控制台上输入数据
- 方法二
- 方法一
- 网络:一组相互连接的计算机