JAVA基础 网络编程技术学习笔记 3

简介: JAVA基础 网络编程技术学习笔记

3.5 一对多应用设计

点对点聊天的缺点:因为客户端和服务端之间的连接是在主线程中,主线程的作用是启动接受和发送消息的线程,一旦主线程任务完成,accpet方法也结束了,那么服务端就无法再接受其他客户端的连接了。此时,只要将accpet单独拿出来,使服务端监听到每个与之连接的客户端,并返回对应的Socket对象即可。但是这样,客户端与客户端之间是隔离的,不能相互通信。

3.5.1 一对多应答型服务端

应答型客户端指的是:一个服务端和多个客户端连接,客户端向服务端发送数据时服务端再将数据返回给客户端。

//主线程
public class EchoServer {
    public static void main(String[] args) {
        try(ServerSocket serverSocket = new ServerSocket(8888)) {
            while (true){
                Socket socket = serverSocket.accept();//不能死
                new Msg(socket).start();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.5.2 一对多聊天应用实现

聊天服务器就是多个客户端和服务端连接,当一个客户端向服务端发送数据的时候,服务端会将这些数据发送给其他和服务端相连的客户端。

服务器的线程设计

实现思路,服务端的接受线程负责接受客户端的消息,接受到消息后将消息写入到公共数据区,与此同时唤醒所有客户端的发送线程将缓冲区中的数据发送给客户端。然后发送线程又处于阻塞状态

package cn.it.bz.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
//接受客户端消息的线程
class ChatReceive extends Thread{
    private Socket socket;//拿到客户端对应的socket对象
    public ChatReceive(Socket socket){
        this.socket = socket;
    }
    public void receiveMsg(){
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())))
        {
            while (true){
                 String s = bufferedReader.readLine();//读消息不受锁影响
                 synchronized ("aaa"){
                    //将读取的数据写入公共数据区
                     ChatRoomServer.buf = "["+socket.getInetAddress()+"]"+s;
                    //唤醒所有发送消息的线程
                     "aaa".notifyAll();
                 }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
      receiveMsg();
    }
}
//向客户端发送消息的线程
class ChatSend extends Thread{
    private Socket socket;//拿到客户端对应的socket对象
    public ChatSend(Socket socket){
        this.socket = socket;
    }
    public void sendMsg(){
       try(PrintWriter printWriter = new PrintWriter(socket.getOutputStream());)
       {
           while (true){
               //加锁,让线程实现同步(互斥)
               synchronized ("aaa"){
                   //让发送消息的线程处于等待状态,只有服务端接受到数据后才能将数据发送出去。
                   "aaa".wait(); //表示拥有“aaa”对象锁的线程处于阻塞状态。
                   printWriter.println(ChatRoomServer.buf);
                   printWriter.flush();
               }
           }
       }catch (Exception e){
           e.printStackTrace();
       }
    }
    @Override
    public void run() {
        sendMsg();
    }
}
//主线程,专门负责客户端和服务端之间的连接
public class ChatRoomServer {
    public static String buf; //公共数据区
    public static void main(String[] args) {
        System.out.println("服务端启动");
        try(ServerSocket serverSocket = new ServerSocket(8888)) {
            while (true){
                Socket socket = serverSocket.accept();//不能死
                System.out.println("连接到:"+socket.getInetAddress());
                new ChatReceive(socket).start(); //启动线程
                new ChatSend(socket).start();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
package cn.it.bz.Socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
//接受消息
class ClientChatReceive extends Thread{
    private Socket socket;
    public ClientChatReceive(Socket socket) {
        this.socket = socket;
    }
    public void accept(){
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(this.socket.getInputStream())))
        {
            while (true){
                System.out.println( bufferedReader.readLine());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        accept();
    }
}
//发送消息
class ClientChatSend extends Thread{
    private Socket socket;
    public ClientChatSend(Socket socket) {
        this.socket = socket;
    }
    public void send(){
        try(PrintWriter printWriter = new PrintWriter(socket.getOutputStream()))
        {
            System.out.println("请输入消息:");
            Scanner scanner = new Scanner(System.in);
            while (true){
                String s = scanner.nextLine();
                printWriter.println(s);
                printWriter.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        send();
    }
}
//聊天室客户端
public class ChatRoomClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1",8888);
            new ClientChatSend(socket).start();
            new ClientChatReceive(socket).start();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

四、UDP通信

4.1 UDP实现原理

UDP协议与之前讲到的TCP协议不同,是面向无连接的,双方不需要建立连接便可通信。UDP通信所发送的数据需要进行封包操作(使用DatagramPacket类),然后才能接收或发送(使用DatagramSocket类)。虽然不可靠,但是效率高,视频会议网络聊天一般使用UDP协议。


DatagramPacket:数据容器(封包)的作用


此类表示数据报包。 数据报包用来实现封包的功能。

image.pngDatagramSocket:用于发送或接收数据报包


当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket对象,在客户端产生一个DatagramSocket对象。服务器端的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接收。


DatagramSocket有两种常用的构造函数。一种是无需任何参数的,常用于客户端;另一种需要指定端口,常用于服务器端。如下所示:

  1. DatagramSocket() :构造数据报套接字并将其绑定到本地主机上任何可用的端口。
  2. DatagramSocket(int port) :创建数据报套接字并将其绑定到本地主机上的指定端口。

常用方法

方法名 使用说明
send(DatagramPacket p) 从此套接字发送数据报包
receive(DatagramPacket p) 从此套接字接收数据报包
close() 关闭此数据报套接字

4.2 UDP通信编程基本步骤

  1. 创建客户端的DatagramSocket,创建时,定义客户端的监听端口。
  2. 创建服务器端的DatagramSocket,创建时,定义服务器端的监听端口。
  3. 在服务器端定义DatagramPacket对象,封装待发送的数据包。
  1. 客户端将数据报包发送出去。
  2. 服务器端接收数据报包。

4.3 实现UDP通信

4.3.1 创建服务端

package cn.it.bz.Socket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
    public static void main(String[] args) {
        //创建基于UDP协议的DatagramSocket对象。
        //表示服务端要监听该电脑的8888端口。
        try(DatagramSocket datagramSocket = new DatagramSocket(8888);)
        {
            //创建数据缓冲区
            byte[] bytes = new byte[1024];
            //创建数据包对象(就相当于是放东西的袋子)
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
            //等待接受客户端发送数据(服务端拿着袋子准备装数据)
            datagramSocket.receive(datagramPacket);
            //取出袋子中的数据
            byte[] data = datagramPacket.getData();
            //将字节数据转换成字符串类型【offset表示从数组哪个位置开始,datagramPacket.getLength()表示袋子中数据包的长度】
            String s = new String(data,0,datagramPacket.getLength());
            System.out.println("客户端发送的数据是:"+s);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.3.2 创建客户端

package cn.it.bz.Socket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UDPClient {
    public static void main(String[] args) {
        //该端口为UDP协议发送数据指定端口。
        // 如果服务端和客户端在一个设备上,那么客户端指定的端口和服务端监听的端口不能相同,否则会发生端口抢占。
        try( DatagramSocket datagramSocket = new DatagramSocket(9999);) {
             //数据需要转换为字节数组类型
             byte[] bytes = "Java".getBytes();
             //创建数据报包对象.后两个参数指定数据发送到哪个服务端以及服务端接受消息的端口
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,new InetSocketAddress("127.0.0.1",8888));
            //向服务端发送数据
            datagramSocket.send(datagramPacket);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.4 基于UDP协议传输基本数据类型

4.4.1 创建服务端

package cn.it.bz.Socket;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class BasicTypeUDPServer {
    public static void main(String[] args) {
        try(DatagramSocket datagramSocket = new DatagramSocket(8888);)
        {
            //字节数组
            byte[] bytes = new byte[1024];
            //存放数据的袋子
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
            //等待接受客户端数据
            datagramSocket.receive(datagramPacket);
            //从袋子取数据
            byte[] data = datagramPacket.getData();
            //通过DataInputStream对象读取基本数据类型
            //ByteArrayInputStream(bytes)可以直接从内存读取字节
            try(DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes))) {
                System.out.println("客户端发送的数据:"+dataInputStream.readLong());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.4.2 创建客户端

package cn.it.bz.Socket;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class BasicTypeUDPClient {
    public static void main(String[] args) {
        long data = 1000000L;
        try(DatagramSocket datagramSocket = new DatagramSocket(9999);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
        )
        {
            //将基本数据类型转换为数组
            dataOutputStream.writeLong(data);
            //将基本数据类型转换为数组
            byte[] bytes = byteArrayOutputStream.toByteArray();
            //数据包装
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,new InetSocketAddress("127.0.0.1",8888));
            //数据发送
            datagramSocket.send(datagramPacket);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.5 基于UDP协议传递自定义对象

4.5.1 创建对象

package cn.it.bz.Socket;
import java.io.Serializable;
//使用流操作对象必须要实现序列化接口
public class Person implements Serializable {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4.5.2 创建服务端

package cn.it.bz.Socket;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ObjectTypeServer {
    public static void main(String[] args) {
        try(DatagramSocket datagramSocket = new DatagramSocket(8888))
        {
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
            datagramSocket.receive(datagramPacket);
            //接受数据
            byte[] data = datagramPacket.getData();
            //将对象转换为字节数组
            try(ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(data)))
            {
                 Person person = (Person) objectInputStream.readObject();
                 System.out.println(person);
            }catch (Exception e){
                e.printStackTrace();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.5.3 创建客户端

package cn.it.bz.Socket;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class ObjectTypeClient {
    public static void main(String[] args) {
        try(DatagramSocket datagramSocket = new DatagramSocket(9999);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//ByteArrayOutputStream将内存的数据读取到字节数组中,将对象转换为字节数组
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream))
        {
            //创建自定义对象
            Person person = new Person("zhangsan",13);
            objectOutputStream.writeObject(person);
            byte[] bytes = byteArrayOutputStream.toByteArray();
            //数据包装
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,new InetSocketAddress("127.0.0.1",8888));
            //发送数据
            datagramSocket.send(datagramPacket);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4b2a078ded704e78970bd24e8681e4e9.png

相关文章
|
11天前
|
存储 安全 网络安全
云计算与网络安全:技术融合的未来之路
【10月更文挑战第30天】在数字化浪潮的推动下,云计算已成为企业信息技术架构的核心。然而,随之而来的网络安全问题也日益凸显。本文将探讨云计算与网络安全的关系,分析云服务中的安全挑战,并提出相应的解决方案。我们将通过实例展示如何在云计算环境中实现网络安全的最佳实践,以期为读者提供一条技术融合的未来之路。
|
2天前
|
云安全 安全 网络安全
云计算与网络安全:技术挑战与解决策略
【10月更文挑战第39天】随着云计算技术的飞速发展,网络安全问题也日益凸显。本文将探讨云计算环境下的网络安全挑战,并提出相应的解决策略。通过分析云服务模型、网络安全威胁以及信息安全技术的应用,我们将揭示如何构建一个安全的云计算环境。
|
2天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
9 2
|
3天前
|
云安全 安全 网络安全
云计算与网络安全:技术融合的未来之路
【10月更文挑战第38天】 在数字化浪潮中,云计算和网络安全成为支撑现代企业和个人数据安全的两大基石。本文将深入探讨云计算服务如何与网络安全技术相结合,保障信息安全,并分析面临的挑战及未来发展趋势。我们将通过实际案例,揭示云安全的最佳实践,为读者提供一条清晰的技术融合路径。
|
3天前
|
安全 网络安全 数据库
云计算与网络安全:技术融合的未来之路
【10月更文挑战第37天】本文将探讨云计算与网络安全的交汇点,揭示它们如何共同塑造信息安全的未来。我们将深入了解云服务的核心组件、网络安全的关键策略以及两者如何相互增强。通过分析当前的挑战和未来的趋势,本文旨在为读者提供一条清晰的路径,以理解并应对这一不断发展的技术领域。
|
5天前
|
网络协议 数据挖掘 5G
适用于金融和交易应用的低延迟网络:技术、架构与应用
适用于金融和交易应用的低延迟网络:技术、架构与应用
26 5
|
6天前
|
存储 安全 网络安全
云计算与网络安全:技术融合与挑战
【10月更文挑战第35天】本文将探讨云计算与网络安全的交叉点,包括云服务、网络安全和信息安全等技术领域。我们将深入了解云计算的基本概念,以及如何通过云服务实现网络安全和信息安全。同时,我们还将讨论云计算面临的安全挑战,并提出相应的解决方案。最后,我们将通过代码示例展示如何在云计算环境中实现网络安全和信息安全。
17 3
|
6天前
|
存储 安全 网络安全
云计算与网络安全:云服务、网络安全、信息安全等技术领域的深度剖析
【10月更文挑战第34天】本文将深入探讨云计算与网络安全的关系,包括云服务、网络安全、信息安全等技术领域。我们将通过实例和代码示例,解析云计算如何改变网络安全的格局,以及如何在云计算环境下保护信息安全。我们将从云计算的基本概念开始,然后深入到网络安全和信息安全的主题,最后通过代码示例来展示如何在云计算环境下实现网络安全和信息安全。
|
8天前
|
供应链 安全 网络安全
区块链技术与网络安全:机遇与挑战
区块链技术与网络安全:机遇与挑战
32 2
|
9天前
|
机器学习/深度学习 移动开发 自然语言处理
HTML5与神经网络技术的结合有哪些其他应用
HTML5与神经网络技术的结合有哪些其他应用
23 3