【Socket】TCP协议、UDP协议的Java入门示例

TCP(Transmission Control Protocol)传输控制协议

面向连接,可靠的基于字节流的传输通信协议,每次通信双方都需要提前建立连接,因此需要三次握手(以下情景均以客户端为数据发出方,服务端为数据接收方)
第一次【客户端进入SYN_SEND状态】
客户端向服务端发送syn包,然后等待服务端回话。
李四:“喂,是张三吗?”

第二次【服务端进入SYN_RECV状态】
服务端收到syn包后,服务端向客户端发送syn+ack包,然后等待客户端回话。
张三:“我是张三,您是李四?”

第三次【双方都进入ESTABLISHED状态】
客户端收到syn+ack包,确认服务端收到了syn包,然后将ack包发送给服务端进行确认。
李四:“我是李四,我有些事情要说,……”

第三次结束后,客户端即开始向服务端传输数据,直到传输完毕后,进行四次挥手结束这个过程。

第一次【客户端向服务端发起请求:我要挂断连接了】
客户端向服务端发送FIN=1的请求,要求断开。
李四:“那就说到这里,我忙去了”

第二次【服务端收到客户端的请求,回复:客户端可以挂断连接】
服务端向客户端发送ACK=1进行确认。
张三:“去吧,这件事我知道了”

第三次【服务端首先断开与客户端的连接,回复:服务端已经准备好】
服务端向客户端发送FIN=1的请求,准备好断开连接了。
张三:“拜拜”

第四次【客户端收到服务端的请求,回复:收到】
客户端向服务端发送ACK=1的请求,然后断开链接,服务端收到后也断开了连接
李四:“拜拜”(挂断)

TCP的Java示例

运行行时,先开启服务端,再开启客户端
可以发送被序列化的对象,常用Map集合进行封装,或者用Json或Base64转码的字符串
服务端前两步骤通常放在一个主线程的循环中,没有请求时,程序停在accept()方法,一旦接收到请求,后续创建线程,在线程中完成第三步开始的步骤

@Test
public void serverTCP() { 
     //服务端
    try {
        //1、创建通信对象,指定端口
        ServerSocket ss = new ServerSocket(8000);
        //2、监听客户端请求
        Socket s = ss.accept();//当前线程进入阻塞状态,等待中
        //3、开启输入流,接收消息
        InputStream is = s.getInputStream();//发送方的信息都在s对象中
        //4、读取接收到的信息
        ObjectInputStream ois = new ObjectInputStream(is);
        List<String> list = (List<String>) ois.readObject();
        for (String str : list) {
            System.out.print(str);//输出
        }
        //5、关闭资源,严格按照先开后关原则关闭,顺序不能改变
        ois.close();
        is.close();
        s.close();
        ss.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

@Test
public void clientTCP() {
    //客户端
    try {
        //1、创建通信对象,指定接收方的地址,IP和端口
        Socket s = new Socket("127.0.0.1", 8000);
        //2、开启输出流,发送请求
        OutputStream os = s.getOutputStream();
        //3、准备消息
        List<String> list = new ArrayList<>();
        list.add("TCP");
        list.add("测试");
        //4、发送消息
        ObjectOutputStream oo = new ObjectOutputStream(os);
        oo.writeObject(list);
        oo.flush();//刷新缓存,信息开始发送
        //5、关闭资源,严格按照先开后关原则关闭,顺序不能改变
        oo.close();
        os.close();
        s.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

UDP(User Datagram Protocol)用户数据报协议

面向事务,适用于可靠性不高,延迟小的传输协议中,如常见的网络直播,在线视频,在线音频,语音等,由于该协议是一种无连接的传输层协议,传输效率高,可靠性不好,网络环境不是很好时容易出现中断,如视频暂停缓冲,语音模糊
发送方只关心谁来请求数据,然后将数据丢出去,至于数据是否完整无损的抵达接收方,不去判断
至于发送方如何得到是谁需要请求数据,这些请求可以通过接收方的APP,通过TCP连接到服务端就可以很轻松的就可以得到了,然后发送方每隔一段时间更新发送的数据到接收方缓存,就可以完成在线直播了

相当于大学上课的情景,老师为发送方,只管讲课,醒着的同学听到考试的重点,做了笔记,还有些睡着的同学什么也没听到,老师当然不会走到那些学生面前叫醒然后重新强调一遍,老师会继续讲下去,一个又一个的知识点就讲过去了

UDP的Java示例

运行时,先开启接收方,再开启发送方
【无论服务端还是客户端,都可以是接收方和发送方,比如开直播:Up主(发送方)与B站(接收方),B站(发送方)与粉丝(接收方)】
由于接收方不知道发送方的数据报的信息,接收方创建数据报时,应该大于发送方的数据容量,或者双方约定好固定的容量。

@Test
public void sendUDP() {
    //发送方
    try {
        //1、创建发送的信息
        List<String> list = new ArrayList<>();
        list.add("UDP");
        list.add("测试");
        //2、创建输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(list);
        //3、信息存入流中
        oos.flush();
        byte[] b = baos.toByteArray();
        if (b == null) return;//不能发送null
        //4、创建数据报,数据,长度,接收方的目标地址,端口
        DatagramPacket dp = new DatagramPacket(
                // b.length=73
                b, b.length, InetAddress.getByName("127.0.0.1"), 8000);
        //5、创建通信对象
        DatagramSocket ds = new DatagramSocket();
        //6、发送数据
        ds.send(dp);
        //7、关闭资源,严格按照先开后关原则关闭,顺序不能改变
        ds.close();
        oos.close();
        baos.close();
    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Test
public void receiveUDP() {
    //接收方
    try {
        //1、创建通信对象,设置监听端口
        DatagramSocket ds = new DatagramSocket(8000);
        //2、创建数据报
        byte[] bytes = new byte[1024];
        //最大和虚拟机配置有关,默认21_1917_2080,接收时,设置的长度不足会抛出EOF异常
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        //3、将接收到的数据写入数据报
        ds.receive(dp);
        //4、创建流转换为对象
        ByteArrayInputStream bais = new ByteArrayInputStream(dp.getData());
        ObjectInputStream ois = new ObjectInputStream(bais);
        //5、获取数据
        List<String> list = (List<String>) ois.readObject();
        for (String str : list) {
            System.out.print(str);//输出
        }
        //6、关闭资源,严格按照先开后关原则关闭,顺序不能改变
        ois.close();
        bais.close();
        ds.close();
    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章