07 - 网络编程(二) 🏄
基于 TCP 的 Socket 通信案例讲解
文件服务器实现

聊天室实现


基于 UDP 协议的 Socket 通信
概述
 UDP(User Datagram Protocol)用户数据报协议,在这种协议之下,通信的双方之间不需要建立稳定可靠的连接,UDP 协议下没有服务端和客户端的概念,只存在发送方和接收方(发送方也可能是接收方:例如飞秋);数据的传输模式是以报文的形式发送(TCP 基于输入输出流);类似于快递包裹的投递,UDP 协议不保证数据发送的安全性,可能会出现数据报包丢失(丢包),导致数据传输的卡顿,但是数据报包的偶尔丢失对于用户来说无关紧要;数据报包的到达也不一定是有序的,可能会出现后发先到;UDP 协议的使用场景十分常见:
例如:
- 视频会议
- 屏幕广播
- 常见的端游
TCP:打电话(必须先接通,才能通话)
UDP:发快递(无需建立稳定连接)
DatagramSocket(UDP 网络通道)
DatagramSocket 用于构建一个无连接的网络通道,通过该对象可以实现消息的发送(send)和消息的接收(receive)。
常见构造器:
- DatagramSocket()
- DatagramSocket(int port)
常见方法:
- send(DatagramPacket dp):发送数据报包
- receive(DatagramPacket dp):接收数据存储到数据报包中
DatagramPacket(数据报包)
DatagramPacket 用于表示数据报包,内部通过字节数组作为缓冲区存储或者接收数据.
常见构造器:
- DatagramPacket(byte[] buff,int len)
- DatagramPacket(byte[] buff,int offset ,int len,InetAddress addr,int port)
常见方法:
- getLength():获取数据报包裹中实际的字节长度
UDP 通信实例
发送方
public class Sender {
    public static void main(String[] args) throws IOException {
        //创建基于UDP协议的网络通道
        DatagramSocket ds = new DatagramSocket();
        //准备需要发送的消息
        String msg = "天王盖地虎,小鸡炖蘑菇!!!";
        //基于需要发送的数据将数据打包成数据报包
        DatagramPacket dp = new DatagramPacket(
                msg.getBytes(),
                0,
                msg.getBytes().length,
                InetAddress.getByName("localhost"),
               	10000
        );
        //发送数据报包
        ds.send(dp);
    }
}
接收方
public class Receiver {
    public static void main(String[] args) throws IOException {
        //占据在指定端口创建网络通道
        DatagramSocket ds = new DatagramSocket(10000);
        //创建字节缓冲区
        byte[] buff = new byte[1024];
        //构建空数据报包用于接收发送方发送的数据
        DatagramPacket dp = new DatagramPacket(buff,buff.length);
        while(true){
            //接收数据
            ds.receive(dp);
            //将发送的文本信息转换为String
            String msg = new String(buff,0,dp.getLength());
            String ip = dp.getAddress().getHostAddress();
            System.out.println(ip+":"+msg);
        }
    }
}
UDP 通信之与飞秋通信实例
飞秋的消息发送实现也是基于 UDP 协议的,如果我们熟悉飞秋消息的组织方式,即可通过编写 Java 程序向飞秋发送消息:
飞秋的消息发送协议如下:
版本号:消息识别号:发送人昵称:发送人主机地址:命令字:消息内容
其中:
版本号固定为
1消息识别号:保持唯一即可(一般使用系统时间)
发送昵称可以任意编写
发送主机地址可伪装,即任意编写
命令字:32 固定表示为消息发送
消息内容:实际需要发送到目标地址的消息内容
例如:
1:100:刘德华:liudehua:32:hello飞秋
public class FeiQUtils {
    /**
     * 飞秋协议版本号,默认:1
     */
    private static final int VERSION_CODE = 1;
    /**
     * 发送人昵称
     */
    private static final String NICK_NAME = "SOFTEEM";
    /**
     * 发送人主机地址:可伪装
     */
    private static final String HOST_NAME = "localhost";
    /**
     * 命令字:32表示发送消息
     */
    private static final int COMM_CODE = 32;
    private DatagramSocket ds;
    private String ip;
    public FeiQUtils(String ip) throws SocketException {
        ds = new DatagramSocket();
        this.ip = ip;
    }
    public void sendMsg(String msg) throws IOException {
        //创建网络通道
        //准备数据
        String content = VERSION_CODE + ":" + System.currentTimeMillis() + ":" + NICK_NAME + ":" + HOST_NAME + ":" + COMM_CODE + ":" + msg;
        //将需要发送的数据打包成数据报包
        DatagramPacket dp = new DatagramPacket(
            content.getBytes("gbk"),
            0,
            content.getBytes("gbk").length,
            InetAddress.getByName(ip),
            2425
        );
        //发送消息
        ds.send(dp);
    }
    public static void main(String[] args) throws IOException {
        FeiQUtils utils = new FeiQUtils("192.168.6.255");
        utils.sendMsg("hello1");
        utils.sendMsg("hello2");
        utils.sendMsg("hello3");
        utils.sendMsg("hello4");
    }
}
UDP 数据广播之 MulticastSocket
在传统的互联网架构之下,一台主机需要将消息发送的多台不同的主机时 中间是经过了交换机(路由器),如果需要进行消息的群发,此时在 TCP 协议之下,发送方需要将消息制作 N 份拷贝发给交换机,然后交换机再将这些消息分发给局域网中其他主机,这样一来,大大增加网络负担。

因此,在 UDP 协议中,提供了组播功能(数据广播),使用 UDP 数据广播,只需要将消息发送一次到交换机,交换机会将消息制作 N 份拷贝,发送到位于该广播组内的所有其他主机。类似如下结构:

使用 UDP 数据广播时,需要提供一个数据广播地址,该地址为 IP 地址的 D 类地址,可选范围为:224.0.0.0~239.255.255.255之间,并且广播消息的发送方和接收方需要加入该广播地址:
发送广播
public class BoradcastSender {
    public static void main(String[] args) throws IOException {
        //准备组播地址
        InetAddress groupIP = InetAddress.getByName("225.6.7.8");
        //创建广播网络通道
        MulticastSocket ms = new MulticastSocket();
        //为网络通道绑定组播地址
        ms.joinGroup(groupIP);
        String msg = "今晚八点半,在村委会有大型歌舞表演,请各位村民端好小板凳准时观看!!!";
        DatagramPacket dp = new DatagramPacket(
            msg.getBytes(),
            0,
            msg.getBytes().length,
            groupIP,
            9999
        );
        //发送广播
        ms.send(dp);
        ms.close();
    }
}
接收广播
public class BordercastReceiver {
    public static void main(String[] args) throws IOException {
        //准备组播地址
        InetAddress groupIP = InetAddress.getByName("225.6.7.8");
        //创建广播网络通道
        MulticastSocket ms = new MulticastSocket(9999);
        //为网络通道绑定组播地址
        ms.joinGroup(groupIP);
        byte[] b = new byte[1024];
        //创建空数据报包
        DatagramPacket dp = new DatagramPacket(b,b.length);
        while(true){
            ms.receive(dp);
            String msg = new String(b,0,dp.getLength());
            System.out.println(dp.getAddress().getHostAddress()+"广播消息:"+msg);
        }
    }
}
作业
- 基于 UDP 协议实现一个聊天室 
- 基于 TCP/IP 协议完成一个远程登陆功能 