当前位置: 首页 > article >正文

Java中基于TCP的Socket编程

一、概述

Socket(套接字)是网络通信的一种机制,允许不同主机之间的进程进行通信。在Java中,Socket支持TCP(传输控制协议)和UDP(用户数据报协议)。

1、TCP协议介绍

TCP协议在通信之前需要先建立连接,数据传输过程中能保证数据的完整性和顺序性。

2、UDP协议介绍

UDP协议可用于发送数据包,所需付出的开销比TCP协议少得多,但是UDP协议传输数据包时不会按照顺序传输,也可能会在数据传输中丢失数据。

二、socket编程步骤

1、创建一个Socket服务端,指定服务要监听的端口

// port为监听端口
ServerSocket socketServer = new ServerSocket(port);

2、监听客户端连接请求并接受连接

Socket socket = serverSocket.accept();

3、通过Socket对象获取输入流,用于读取客户端发送的数据

InputStream inputStream = socket.getInputStream();

完整代码:

InputStream inputStream = socket.getInputStream();
int len;
byte[] data = new byte[1024 * 1024];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while ((len = inputStream.read(data)) != -1) {
    byteArrayOutputStream.write(data, 0, len);
}
log.info("响应数据:{}", new String(byteArrayOutputStream.toByteArray()));

4、通过Socket对象获取输出流,用于向客户端发送数据

OutputStream outputStream = socket.getOutputStream();

完整代码:

// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("客户端1写入数据".getBytes());
// 超时时间,单位:毫秒
socket.setSoTimeout(timeout);
// 结束写入
socket.shutdownOutput();

5、创建Socket客户端,与客户端建立连接

// ip:服务端ip,port:服务端端口
Socket socket  = new Socket(ip, port);

三、socket服务端和socket客户端的完整代码

1、socket服务端

package com.xiaobai.java_core.socket;

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Author 王天文
 * @Date 2024/12/15 17:02
 * @Description:
 */
@Slf4j
public class SocketServerBIO {

    // 服务端监听器端口
    private static final int SERVER_PORT = 15000;

    public static void main(String[] args) {

        ServerSocket serverSocket = null;
        try {
            // 服务端绑定监听器端口
            serverSocket = new ServerSocket(SERVER_PORT);
            log.info("服务端启动,等待客户端连接,监听端口:{}", SERVER_PORT);

            while (true) {
                try {
                    // 获取新的连接
                    Socket socket = serverSocket.accept();
                    log.info("客户端已连接,端口:{}", socket.getPort());

                    // 每个连接创建一个线程(可使用ThreadPoolTaskExecutor或ThreadPoolExecutor创建线程)
                    Thread connectThread = new Thread(new Runnable() {
                        @Override
                        public void run() {

                            String connectThreadName = Thread.currentThread().getName();
                            log.info("连接线程名称,{}", connectThreadName);

                            try {
                                // 按字节流方式读取数据
                                InputStream socketInputStream = socket.getInputStream();
                                int len;
                                byte[] data = new byte[1024 * 1024];
                                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                                while ((len = socketInputStream.read(data)) != -1) {
                                    byteArrayOutputStream.write(data, 0, len);
                                }
                                log.info("客户端消息:{}", new String(byteArrayOutputStream.toByteArray()));

                                // 客户端响应
                                String responseMsg = "客户端响应消息" + "_" + connectThreadName;
                                OutputStream socketOutputStream = socket.getOutputStream();
                                log.info("客户端响应消息:{}", responseMsg);
                                socketOutputStream.write(responseMsg.getBytes());

                                // 服务端结束写入
                                socket.shutdownOutput();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                        }
                    });
                    connectThread.start();

                    // 判断连接是否断开
                    if (socket.isClosed()) {
                        log.info("客户端连接已断开");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2、socket客户端

package com.xiaobai.java_core.socket;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.Socket;

/**
 * @Author 王天文
 * @Date 2024/12/15 18:02
 * @Description:
 */
@Slf4j
public class SocketClient {
    private static final String SERVER_IP = "127.0.0.1";
    private static final int SERVER_PORT = 15000;
    private static final int timeout = 1800000;

    public static void main(String[] args) {

        Socket socket = null;
        try {
            // 与服务端建立连接
            socket  = new Socket(SERVER_IP, SERVER_PORT);

            // 发送数据
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("客户端1写入数据".getBytes());
            // 超时时间,单位:毫秒
            socket.setSoTimeout(timeout);
            // 结束写入
            socket.shutdownOutput();

            // 获取响应数据
            InputStream inputStream = socket.getInputStream();
            int len;
            byte[] data = new byte[1024 * 1024];
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while ((len = inputStream.read(data)) != -1) {
                byteArrayOutputStream.write(data, 0, len);
            }
            log.info("响应数据:{}", new String(byteArrayOutputStream.toByteArray()));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

四、说明

1、阻塞问题

(1)BIO模式是同步阻塞I/O模式,数据的读取和写入必须阻塞在一个线程内等待完成;
(2)调用accept()方法后,当前线程处于挂起状态,系统层面得不到这个线程的运行权,其他客户端会处于阻塞状态,直到当前线程运行完毕;

2、解决方案

在服务端和客户端建立连接后,需要新建线程处理任务,使用线程池创建线程可防止内存溢出,提高线程的复用率,节省CPU资源。

学习小记 – 终于把线程状态流转理清楚了
Java面试常考的 BIO,NIO,AIO 总结

测试代码地址:
https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/java_core/socket


http://www.kler.cn/a/442821.html

相关文章:

  • Kutools for Excel 简体中文版 - 官方正版授权
  • Redis 优化秒杀(异步秒杀)
  • 持续交付的利器:Blue Ocean与Pipeline
  • ASP.NET Core 中使用 Cookie 身份验证
  • 隐私计算,构建安全的未来数据空间
  • Markdown中甘特图的使用
  • API开发:Flask VS FastAPI
  • 基于RK3588机器人控制器+3D视觉传感器的送餐机器人解决方案
  • 网络编程 02:IP 地址,IP 地址的作用、分类,通过 Java 实现 IP 地址的信息获取
  • 用 Python 格式化器重新定义用户体验
  • open-cv机器视觉相关知识
  • 蓝桥杯刷题——day6
  • 心法利器[122] | 算法面试的八股和非八股讨论
  • 借 SSM 之力,以 Vue 为笔绘就新锐台球厅管理系统设计与实现蓝图
  • NDRCContextUnmarshall断点函数分析之I_RpcBindingCopy函数的作用
  • oracle控制文件发生变化的情况
  • 介绍几个Linux下的杀毒软件
  • 重新定义页签!Choerodon UI Tabs让管理更高效
  • Vue-Form-Making:Star5.5k,一款强大的Vue表单设计器,适用于低代码平台、自定义表单
  • ABAP SQL 取日期+时间最新的一条数据
  • Next.js搜索引擎优化:框架级别的搜索引擎优化能力
  • 【Redis】Redis缓存击穿
  • (3)spring security - 认识PasswordEncoder
  • 大厂面试智力题大全(详细解题思路,持续更新)
  • 【map与set】—— 我与C++的不解之缘(二十二)
  • Redis内存淘汰策略有哪些