一、网络相关概念
1、认识网络
1.网络的概念和分类
简单来说,网络就是连接在一起共享数据和资源的一组计算机。
计算机网络旨在实现数据通信。如文本、图片或声音。
按照地理覆盖范围,计算机网络可以划分为局域网(LAN)、城域网(MAN)和广域网(WAN)。
2.网络分层模型
国际化标准组织(ISO)于1984年颁布了开放系统互连(OSI)参考模型。OSI参考模型是一个开放式体系结构,它规定将网络分为7层,每层在网络信息传递中都发挥不同的作用。还有一个TCP/IP五层模型。TCP/IP是传输控制协议/网络互连协议的简称。应用层对应OSI参考模型的最高3层(应用层、表示层、会话层),别的和OSI的一致。
2、IP地址
1.IP地址自述
要实现两台计算机通信,双方都要有地址,在网络中使用一种具有层次结构的逻辑地址来标识一台主机,这个地址成为IP地址。IP地址是用来唯一标识网络中的每一台计算机。
IP地址目前存在IPv4和IPv6两种标准。
2.IP地址的组成和分类
(1)IP地址的组成
IPv4地址有32位,由4个8位的二进制数组成,每8位之间用圆点隔开,如11000000.10101000.00000010.00010100。但由于二进制不方便记忆,所以通常转换成十进制表示,如196.168.2.20。一个地址通常由用3个点号分开的4个十进制表示,称为点分十进制。
IPv6地址有128位,由8个16位的无符号整数组成,每个整数用4个16进制数表示,用:隔开。
(2)IP地址的分类
IP地址包含网络地址和主机地址两部分。其中,网络地址决定了可以分配的最大网络数,主机地址决定了一个网络中可以存在的计算机的最大数量。
特殊地址:
0.0.0.0:表示本机。
127.0.0.1:表示本机回环地址,通常用在本机上ping此地址来检查TCP/IP协议安装是否正确。
255.255.255.255:表示当前子网,一般用于向当前子网广播信息。
(3)IP地址的分配和检测
1)打开“控制面板”窗口,找到“网络和共享中心”图标,点击。
2)找到“更改适配器设置”,如图
3)第一个是你连接的无线网,右键选择属性,找到Internet协议版本4,双击,可以配置TCP/IP地址。
提示:一般电脑会默认自动获得IP地址,因为在一个局域网内,地址是不允许重复的,所以如果你固定了地址,那么很容易和别人的地址产生冲突,从而导致你网络连接不通。
配置完地址以后,可以查看自己的网络连接是否通畅。
二、实现基于TCP协议的Socket编程
1、了解Socket
Socket相当于快递站点,不论发快递还是收快递都需要经过快递站点。
Socket的底层机制复杂,Java平台提供了一些简单的API,可以更简单有效的使用Socket开发而无需了解底层机制。
通信链路的端点就被称为“套接字”(英文名Socket)
是提供给应用程序的接口
java.net包提供了若干支持基于套接字的客户端 / 服务器通信的类。
Socket 、ServerSocket 、DatagramPacket、 DatagramSocket、 InetAddress等等。
2、基于TCP协议的Socket编程
java.net包的两个类Socket和ServerSocket,分别用来实现双向安全连接的客户端和服务器端,类似打电话,只有双方都接通了,才能开始通话。
2.1 实现单用户登录
先写一个用户类,存储用户信息,姓名、密码、性别、电话、发送的信息。注意要实现Serializable 接口,将信息序列化。
//帮我创建setter/getter方法,不会生成有参构造和无参构造 @Data @AllArgsConstructor//生成有参构造 @NoArgsConstructor//生成无参构造 public class User implements Serializable { private String name; private String password; private String gender; private String phone; private String message;
}
服务器端类
public class LoginSocketTcp { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8081);//建立连接,监听端口号 System.out.println("-----------------服务器服务器-------------------"); Socket socket = serverSocket.accept();//使用accept方法等待客户端发起通信 //打开输入输出流 InputStream in = socket.getInputStream(); //反序列化信息 ObjectInputStream ois = new ObjectInputStream(in); User user = (User) ois.readObject();//读取客户端发送的信息,用User类型接收,此时需要进行强转。 OutputStream os = socket.getOutputStream(); //存储给客户端回复的信息 String response = ""; //判断用户信息账号密码是否正确,然后返回信息给客户端 if ("班班".equals(user.getName()) && "123456".equals(user.getPassword())) { System.out.println("客户端登陆成功"); System.out.println("客户端信息是:" + user); System.out.println("客户端发送的信息为:" + user.getMessage()); response = "登录成功!允许使用服务器!"; }else { response = "登录失败!账号或密码错误!"; } //反馈信息给客户端,即向输入流中写入信息 os.write(response.getBytes(StandardCharsets.UTF_8)); //关闭资源 os.flush(); os.close(); ois.close(); in.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
客户端
public class LoginTcpTest { static Scanner sc = new Scanner(System.in); public static void main(String[] args) { try { //建立连接,指定服务器的位置为本机以及端口为8081 Socket socket = new Socket("127.0.0.1",8081); System.out.println("------------------登录验证-------------------"); //打开输入输出流 OutputStream os = socket.getOutputStream(); //序列化信息 ObjectOutputStream oos = new ObjectOutputStream(os);//传对象用ObjectOutputStream InputStream is = socket.getInputStream(); //获取服务器端回复的信息 BufferedReader bf = new BufferedReader(new InputStreamReader(is));//为了使用readLine()方法读取服务器端回复的数据 User user = new User("班班","123456","男","15286999596","错了,我是女生!"); oos.writeObject(user);//将用户的信息传送给服务器端 String info = bf.readLine(); System.out.println(info); //关闭输入输出流 bf.close(); is.close(); oos.flush(); oos.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); System.out.println("连接到服务器失败!"); } } }
运行结果
服务器端
客户端
2.2 实现多客户端用户登录
多客户端登录需要采用多线程的方式解决问题,可以在服务器端创建一个专门负责监听的应用主程序服务,一个专门负责相应的线程程序。
主程序代码
public class LoginThreadTest { public static void main(String[] args) { ServerSocket serverSocket = null; try { //建立一个服务器,指定端口并开始监听 serverSocket = new ServerSocket(8081); System.out.println("-----------------服务器服务器-------------------"); //使用accept方法等待客户端发起通信 Socket socket = null; while (true){ socket = serverSocket.accept(); //将socket对象传进去 LoginThread loginThread = new LoginThread(socket); Thread thread = new Thread(loginThread); thread.start(); } } catch (IOException e) { e.printStackTrace(); } } }
负责相应的线程程序
public class LoginThread implements Runnable{ Socket socket = null; //每启动一个线程,连接对应的Socket public LoginThread(Socket socket){ this.socket = socket; } //启动线程,即响应客户请求 @Override public void run() { try { //打开输入输出流 InputStream in = socket.getInputStream(); OutputStream os = socket.getOutputStream(); //反序列化信息 ObjectInputStream ois = new ObjectInputStream(in); //获取客户端信息 User user = (User) ois.readObject(); //给客户端一个反馈 String response = ""; if ("班班".equals(user.getName()) && "123456".equals(user.getPassword())) { System.out.println("客户端登陆成功"); System.out.println("客户端信息是:" + user); System.out.println("客户端发送的信息为:" + user.getMessage()); response = "登录成功!允许使用服务器!"; }else { response = "登录失败!账号或密码错误!"; } os.write(response.getBytes(StandardCharsets.UTF_8)); //关闭资源 os.flush(); os.close(); ois.close(); in.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
运行结果
客户1:
客户2:
3、实现基于UDP协议的Socket编程
1.TCP和UDP的对比
TCP | UDP | |
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
基于TCP的网络通信是安全的,是双向的,需要先建立连接,才开始数据通信。
基于UDP的网络通信,只需要指明对方地址,然后将数据送出去,并不会事先连接。
2.DatagramPacket类和DatagramSocket类
java中有两个可使用的数据报实现通信的类,即DatagramPacket和DatagramSocket。DatagramPacket类起到数据容器的作用,DatagramSocket类用于发送或接收DatagramPacket。<二者的关系>
DatagramPacket不提供发送或接收数据的类,而DatagramSocket提供send()和recive()方法,用于通过套接字发送和接收数据报。
3.基于UDP协议的Socket编程
服务器端
public class ServiceSocketUdp{ public static void main(String[] args) { //定义byte数组存储接收的信息 byte[] bytes = new byte[1024]; //创建DatagramPacket对象,用来接收数据包 DatagramPacket dp = new DatagramPacket(bytes, bytes.length); try { //创建DatagramSocket对象,接收数据 DatagramSocket socket = new DatagramSocket(8082); socket.receive(dp); System.out.println("已接收"); //显示接收的信息 String info = new String(bytes,0,bytes.length); System.out.println("客户端:"+ info); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
客户端
public class ClientSocketUdp{ public static void main(String[] args) { //要发送的信息 String info = "小白兔,快开门!妈妈回来啦!"; //使用byte数组存储信息 byte[] bytes = info.getBytes(StandardCharsets.UTF_8); try { //获取本机主机地址 InetAddress inetAddress = InetAddress.getByName("localhost"); //创建DatagramSocket对象,向服务器发送信息 DatagramSocket socket = new DatagramSocket(); //创建DatagramPacket对象,封装要发送的信息 DatagramPacket dp = new DatagramPacket(bytes, bytes.length,inetAddress,8082); //发送信息 socket.send(dp); System.out.println("发送成功"); } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
运行结果
客户端
服务器端