基于 Java 简单实现 Socket 通信

本文涉及的产品
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据同步 1个月
简介: 基于 Java 简单实现 Socket 通信


一、什么是 Socket


百科:


image.png


白话就是,基于网络通信协议所制定的交互接口(接口就是规范)。


既然是接口,那必然就有实现,所以本次所有讲述的就是用 Java 来实现的一种 Socket 通信。但实现之前还是要铺垫一下两个网络通信中的重要协议:TCP/IP、UDP。


二、网络通信协议


在计算机中,两台设备要进行通信那就必须是在相同的协议中才能正常通信,而网络中经常本人说的两个协议就是 TCP、UDP。


2.1 面向连接的 TCP

“面向连接”就是在正式通信前必须要与对方建立起连接,是按照电话系统建模的。比如你给别人打电话,必须等线路接通了、对方拿起话筒才能相互通话。


TCP 协议是一种可靠的、一对一的、面向有连接的通信协议,TCP主要通过下列几种方式保证数据传输的可靠性:


在使用TCP协议进行数据传输时,往往需要客户端和服务端先建立一个“通道“、且这个通道只能够被客户端和服务端使用,所以TCP传输协议只能面向一对一的连接。


为了保证数据传输的准确无误,TCP传输协议将用于传输的数据包分为若干个部分(每个部分的大小根据当时的网络情况而定),然后在它们的首部添加一个检验字节。当数据的一个部分被接收完毕之后,服务端会对这一部分的完整性和准确性进行校验,校验之后如果数据的完整度和准确度都为100%,在服务端会要求客户端开始数据下一个部分的传输,如果数据的完整性和准确性与原来不相符,那么服务端会要求客户端再次传输这个部分。


客户端与服务端在使用TCP传输协议时要先建立一个“通道”,在传输完毕之后又要关闭这“通道”,前者可以被形象地成为“三次握手”,而后者则可以被称为“四次挥手”。


TCP 协议能为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地发往网络上的其他计算机,对可靠性要求高的数据通信系统数据通信系统往往使用 TCP 协议传输数据。


2.2 无连接的 UDP 协议

“无连接”就是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。与手机短信非常相似:你在发短信的时候,只需要输入对方手机号就OK了。


UDP 传输协议是一种不可靠的、面向无连接、可以实现多对一、一对多和一对一连接的通信协议。UDP在传输数据前既不需要建立通道,在数据传输完毕后也不需要将通道关闭。只要客户端给服务端发送一个请求,服务端就会一次性地把所有数据发送完毕。UDP在传输数据时不会对数据的完整性进行验证,在数据丢失或数据出错时也不会要求重新传输,因此也节省了很多用于验证数据包的时间,所以以UDP建立的连接的延迟会比以TCP建立的连接的延迟更低。UDP不会根据当前的网络情况来控制数据的发送速度,因此无论网络情况是好是坏,服务端都会以恒定的速率发送数据。虽然这样有时会造成数据的丢失与损坏,但是这一点对于一些实时应用来说是十分重要的。基于以上三点,UDP在数据传输方面速度更快,延迟更低,实时性更好,因此被广泛地用于通信领域和视频网站当中。


UDP 适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间 TCP/IP 通信是否正常,其实 “ping” 命令的原理就是向对方主机发送 ICMP 数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。


例如,在默认状态下,一次 “ping” 操作发送4个数据包。大家可以看到,发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效率高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。


2.3 两者区别

TCP/IP 和UDP最大的区别就是:TCP/IP是面向连接的,UDP是无连接的。TCP协议和UDP协议各有所长、各有所短,适用于不同要求的通信环境。TCP协议和UDP协议之间的差别如下表所示。


在实际的使用中,TCP主要应用于文件传输精确性相对要求较高且不是很紧急的情景,比如电子邮件、远程登录等。有时在这些应用场景下即使丢失一两个字节也会造成不可挽回的错误,所以这些场景中一般都使用TCP传输协议。由于UDP可以提高传输效率,所以UDP被广泛应用于数据量大且精确性要求不高的数据传输,比如我们平常在网站上观看视频或者听音乐的时候应用的基本上都是UDP传输协议。


对比图:


image.png


三、基于 TCP/IP 的 Socket 通信


简单需求:实现客户端与服务端之间基本通信,客户端发一条消息服务端显示客户端消息并回复客户端。


先写服务 code

public class TcpSocketServer {
    /**
     * 服务端程序
     */
    public void server() throws IOException {
        Scanner scanner = new Scanner(System.in);
        // 服务端监听 9528 端口
        ServerSocket serverSocket = new ServerSocket(9528);
        System.out.println("等待连接");
        Socket client = serverSocket.accept();
        System.out.println("连接成功!");
        while (true) {
            // 获取客户端输入流
            InputStream inputStream = client.getInputStream();
            byte[] bytes = new byte[1024];
            int read = inputStream.read(bytes);
            // 客户端发来的消息
            System.out.println("客户端:" + new String(bytes, 0, read, Charset.defaultCharset()));
            // 给客户端发端东西
            System.out.print("请输入:");
            String nextLine = scanner.next();
            if ("out".equals(nextLine)) {
                break;
            }
            client.getOutputStream().write(nextLine.getBytes(StandardCharsets.UTF_8));
        }
    }
    public static void main(String[] args) throws IOException {
        TcpSocketServer tcpSocketServer = new TcpSocketServer();
        tcpSocketServer.server();;
    }
}


再写客户端 code

public class TcpSocketClient {
    /**
     * 客户端程序
     */
    public void client() throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("等待连接服务端!");
        Socket socket = new Socket("127.0.0.1", 9528);
        System.out.println("连接服务端成功!");
        while (true) {
            // 给服务端发点东西
            System.out.print("请输入:");
            String s = scanner.next();
            if ("out".equals(s)) {
                break;
            }
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(s.getBytes(StandardCharsets.UTF_8));
            byte[] bytes = new byte[1024];
            // 读一下服务端发来的东西
            InputStream inputStream = socket.getInputStream();
            int read = inputStream.read(bytes);
            System.out.println("服务端:" + new String(bytes, 0, read, Charset.defaultCharset()));
        }
    }
    public static void main(String[] args) throws IOException {
        TcpSocketClient tcpSocketServer = new TcpSocketClient();
        tcpSocketServer.client();
    }
}


效果


image.png


四、基于 UDP 的 Socket 通信


同理,用UDP的方式实现一下上面的需求


服务端code

public class UdpSocketServer {
    /**
     * 服务端程序
     */
    public void server() throws IOException {
        Scanner scanner = new Scanner(System.in);
        // 服务端监听 9528 端口
        DatagramSocket serverSocket = new DatagramSocket(9528);
        while (true) {
            // 获取客户端输入流
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
            System.out.println("****服务器端已经启动,等待客户端发送数据");
            serverSocket.receive(datagramPacket);
            // 客户端发来的消息
            System.out.println("客户端:" + new String(datagramPacket.getData(), 0, datagramPacket.getLength(), Charset.defaultCharset()));
            // 给客户端发端东西
            System.out.print("请输入:");
            String nextLine = scanner.next();
            if ("out".equals(nextLine)) {
                break;
            }
            byte[] bytes1 = nextLine.getBytes(StandardCharsets.UTF_8);
            DatagramPacket datagramPacket1 = new DatagramPacket(bytes1, bytes1.length, datagramPacket.getAddress(), datagramPacket.getPort());
            serverSocket.send(datagramPacket1);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpSocketServer tcpSocketServer = new UdpSocketServer();
        tcpSocketServer.server();
        ;
    }
}


客户端 code

public class UdpSocketClient {
    /**
     * 客户端程序
     */
    public void client() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            DatagramSocket datagramSocket = new DatagramSocket();
            datagramSocket.connect(new InetSocketAddress("127.0.0.1", 9528));
            String next = scanner.next();
            if ("out".equals(next)) {
                break;
            }
            byte[] bytes = next.getBytes(StandardCharsets.UTF_8);
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
            datagramSocket.send(datagramPacket);
            // 获取客户端输入流
            byte[] bytes1 = new byte[1024];
            DatagramPacket datagramPacket1 = new DatagramPacket(bytes1, bytes1.length);
            datagramSocket.receive(datagramPacket1);
            // 客户端发来的消息
            System.out.println("服务端:" + new String(datagramPacket1.getData(), 0, datagramPacket1.getLength(), Charset.defaultCharset()));
        }
    }
    public static void main(String[] args) throws IOException {
        UdpSocketClient tcpSocketServer = new UdpSocketClient();
        tcpSocketServer.client();
    }
}


效果:


image.png


上述代码如果严谨点,应该在退出的时候关闭流操作,但自己知道就行这也是为了代码简便省略了多余的关闭流操作而多写很多判断代码,但实际开发的时候注意这一点就行了。


五、最后


对于 Socekt 编程,也即网络编程,我们可以看到不论是来连接,或者读取数据都是阻塞的要等待一定时间才能继续往下执行。


如果在主线程中这样做的话,那对于程序而言是非常糟糕的一件事情而且用 Java 所以共的原生 API 去开发一个网络程序是非常繁琐的,当然精通 Socket 的高级工程师除外。所以 NIO 中就对 Socket 编程做了进一步的封装提供了更好的编程方法来提升程序效率,减小开发成本,下篇我们就来说它。


好了,今天的内容到这里就结束了,关注我,我们下期见


相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
目录
相关文章
|
4月前
|
Python
python socket 简单通信
python socket 简单通信
50 1
|
4月前
|
网络协议 安全 网络安全
网络编程:基于socket的TCP/IP通信。
网络编程:基于socket的TCP/IP通信。
290 0
|
2月前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
42 1
[Java]线程生命周期与线程通信
|
1月前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
40 3
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
Java 流计算
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
47 1
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
|
2月前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
34 1
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
25 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
55 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
43 1