JavaSE-网络通信

一、概述

基本的通信架构有2种形式:

  • CS架构( Client客户端/Server服务端)
  • BS架构(Browser浏览器/Server服务端)
    在这里插入图片描述
    IPV4共32bit,4字节,形如192.168.000.001
    IPV6共128bit,16字节,形如2001:0db8:0000:0023:0008:0800:200c:417a
    IPV4形为4位十进制数,每个数字基于8bit,范围0~255
    IPV6形为8位十六进制数,每个数基于16bit,范围0000~FFFF(0 ~ 65535)
    使用Java自带的java.net.InetAddress 可以互动IP
    在这里插入图片描述
    前两个为构造器,后三个为方法

二、端口和协议

在这里插入图片描述
在这里插入图片描述

UDP

不事先建立连接,发送方直接发送,不管对面状态,是不可靠连接。但是通信效率高,适合诸如视频直播、语音通话等。单个数据包最大限制为64KB。

TCP

事先通过三次握手建立连接,可靠连接,四次挥手断开连接,在不可靠信道做到可靠连接
在这里插入图片描述
在这里插入图片描述
三次握手是要Client和Server知道对方可以‘收’和‘发’
第一次握手:S知道C可以发
第二次握手:C知道S可以收发
第三次握手:S知道C可以收发
之后传输数据时,C也不断向S发送确认消息,S需要回复确认,如果没有,那么C会采取重发等措施。
在这里插入图片描述
在这里插入图片描述

三、UDP编程

在这里插入图片描述
在这里插入图片描述
S和C都需要先创建数据包类,用来接收信息。
S代码如下

public class Server {
    public static void main(String[] args) throws Exception {
        //1. create server socket
        DatagramSocket socket = new DatagramSocket(8889);
        //2. create packet
        byte[] bytes = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        while (true) {
            //3.receive
            socket.receive(packet);

            System.out.println("服务端收到了长度为 "+packet.getLength()+" bytes的数据");
            System.out.println("数据为: "+new String(bytes,0, packet.getLength()));
            System.out.println("消息来自:\nIP->"+packet.getAddress().getHostAddress());
            System.out.println("Name->"+packet.getAddress().getHostName());
            System.out.println("Port->"+packet.getPort());
            System.out.println("-----------------------------");
        }

//        socket.close();
    }
}

S会在socket.receive一直等待C的消息,同时通过packet也可以查看发送方的一些信息。
C代码如下

public class Client {
    public static void main(String[] args) throws Exception {
        //1. create client
        DatagramSocket socket = new DatagramSocket();
        Scanner sc = new Scanner(System.in);
        while (true){
            System.out.println("请说:");
            String s = sc.nextLine();
            if ("exit".equals(s)){
                System.out.println("欢迎下次使用~");
                break;
            }
            //2. create data package
            byte[] bytes = s.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),8889);
            //3.send
            socket.send(packet);
            System.out.println("客户端发送了长度为 "+bytes.length+" bytes的数据");
            System.out.println("数据为: "+new String(bytes));
            System.out.println("----------------------");
        }
        socket.close();
    }
}

你可能会好奇为什么S可以在循环外创建packet重复使用而C每次循环都要创建新的?
因为C每次发送的信息长度是未知的,而packet创建需要给出length所以C每次发送都需要重新创建,同时S对C发送的消息长度也是未知的,但是由于UDP对数据包有64KB上限制约,所以我们可以直接创建个64KB大的packet来接收,然后截断字节就可以。

四、TCP编程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
TCP中C由Socket类创建,而S由ServerSocket类创建,然后通过accept方法再次获得Socket类。

S代码如下

    public static void main(String[] args) throws Exception {
        //1.create serversocket
        ServerSocket serverSocket = new ServerSocket(8889);
        //2.wait client link request
        Socket socket = serverSocket.accept();
        //3.get input is for receive message
        InputStream is = socket.getInputStream();
        //4.package low is to high is
        DataInputStream dis = new DataInputStream(is);
        while (true) {
            try {
                //5.receive message
                System.out.println("消息来自: "+socket.getRemoteSocketAddress());
                String rs = dis.readUTF();
                System.out.println("消息为:"+rs);
            } catch (IOException e) {
                System.out.println("客户端离线了");
                break;
            }
        }
        //close
        dis.close();
        socket.close();
    }
}

S会在accept等待C
C代码如下

public class Client {
    public static void main(String[] args) throws Exception {
        //1.create socket and send link request to server
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8889);
        //2.get socket output for write message to server
        OutputStream os = socket.getOutputStream();
        //3.package low os to high os
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说");
            String s = sc.nextLine();
            if (s.equals("exit")){
                break;
            }
            //4.write message
            dos.writeUTF(s);
            dos.flush();//立刻发送
            System.out.println("已发送~");
        }

        //close
        dos.close();
        socket.close();
    }
}

通过高级流包装原始流,方便编程,这里用的是数据流,将字符串以UTF-8的编码形式传输。

五、QA

TCP怎么实现多个C对一个S?

一个S应对多个C的TCP通信,S需要线程池,每个通信都抛给子线程处理即可。在ServerSocket.accept到一个C的适合,就把这个socket抛到子线程。

TCP怎么实现群聊?

C除了需要发送信息,还需要接收信息,并且两个行为需要持续开启,所以C需要多线程,一个从用户键盘读取信息发送,一个从S接收其他C发送的信息。
S除了线程池外,还需要一个子线程都能访问共享数组,来保存所有socket,子线程收到对应的C的消息,就抄一份发给所有socket。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值