Java网络通信TCP

目录

TCP两个核心类

服务端

1.用ServerSocker类创建对象并且手动指定端口号

2.accept阻塞连接服务端与客户端

3.给客户端提供处理业务方法

4.处理业务

整体代码

客户端

1.创建Socket对象,并连接服务端的ip与端口号

2.获取Socket流对象,写入数据,阻塞等待服务端响应

整体代码

jconsole用来监控java线程


TCP两个核心类

ServerSocket 服务器使用socket

accept没有参数 返回值是一个Socket对象

功能是等待服务器和客户端建立连接,建立成功后则会把这个连接获取到进程中。接下来就通过Scoket返回的对象来进行交互

Scoket服务器和客户端都使用socket

通过socket对象就可以进行发送接收数据

socket内部包含了输入流对象(接收) 输出流对象 (发送)

服务端

1.用ServerSocker类创建对象并且手动指定端口号

    private ServerSocket serverSocket=null;

    public Test1(int port) throws IOException {
        serverSocket=new ServerSocket(port);

2.accept阻塞连接服务端与客户端

当我们用UDP那样连接客户端会出现一个问题

UDP是无连接的,他发送数据时直接用地址端口号发送

而TCP是有连接的,当我们用accept连接时,ProcessConnect方法会一直被循环执行单个客户端,而其他客户端要连接时,无法执行到accept,必须等待ProcessConnect方法执行结束,所以会被阻塞。简单来说就是当有一个客户端连接时,其他客户端必须等待连接的客户端断开连接才能连接,还是一个个来连接。

所以ProcessConnect方法我们直接交给线程去执行,这样其他客户端来连接时,直接让线程去执行处理客户端业务。

所以我们可以用到多线程

既然用到了多线程,就可以用出线程池,效率会更高。

public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //阻塞等待服务器与客户端建立连接
            Socket socket=serverSocket.accept();

            //若没线程 当一个客户端连接后 会一直在ProcessConnect内
            //其他客户端连接时 必须要等待ProcessConnect结束,进入下一次循环,才能执行到Socketsocket=serverSocket.accept(); 才能连接下一个客户端
//           // ProcessConnect(socket);


            //所以执行处理客户端请求,我们让线程去干,这样就不会在一个线程内阻塞
//            Thread thread=new Thread(()->{
//                try {
//                    ProcessConnect(socket);
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//            });
//            thread.start();

            //线程池
            ExecutorService executorService= Executors.newCachedThreadPool();
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        ProcessConnect(socket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

3.给客户端提供处理业务方法

我们可以直接获取Socket内的流对象,直接写入读出即可

需要注意,写入数据时,因为时流对象接收,要\n等待其他结束,而我们输入数据时,流对象是不会写入\n的,所以我们可以在数据后自动添加\n 或者用println默认有个\n

public void ProcessConnect(Socket socket) throws IOException {
        //实际他们交流数据是一个为socket类型的文件
        System.out.printf("[地址:%s:端口号%d]建立连接成功\n",socket.getInetAddress().toString(),socket.getPort());
        try (InputStream inputStream = socket.getInputStream();//获取socket内部的input流
             OutputStream outputStream = socket.getOutputStream())//获取socket内部的output流
        {
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            //长连接写法
            while (true) {
                if(!scanner.hasNext()){
                    System.out.printf("[地址:%s:端口号%d]断开连接",socket.getInetAddress().toString(),socket.getPort());
                }
                //从客户端接收数据
                String resqust = scanner.next();
                System.out.println("服务器接收数据");

                //处理数据
                String response = process(resqust);
                System.out.println("服务器处理数据");

                //因为这用流接收 我们按下换行是被scanner接收但并未添加到数据内 而服务器接收流收到的数据内
                //是没有换行符的 就会一直阻塞 所以我们要在数据后再加个\n
                //服务器发送数据
                //printWriter.write(response+'\n');
                //或者直接使用println自带\n
                printWriter.println(response);
                System.out.println("服务器发送数据");

                //刷新缓冲区
                printWriter.flush();
                System.out.printf("[地址:%s:端口号:%d]接收数据:%s 响应数据:%s",socket.getInetAddress().toString(),socket.getPort(),resqust,response);
            }
        }
    }

4.处理业务

这里是为了演示TCP连接,所以只写个简单回传

public String process(String s){
        return s;
    }

整体代码

package TestTCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//服务器
public class Test1 {
    private ServerSocket serverSocket=null;

    public Test1(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //阻塞等待服务器与客户端建立连接
            Socket socket=serverSocket.accept();

            //若没线程 当一个客户端连接后 会一直在ProcessConnect内
            //其他客户端连接时 必须要等待ProcessConnect结束,进入下一次循环,
            // 才能执行到Socketsocket=serverSocket.accept(); 才能连接下一个客户端
//            ProcessConnect(socket);


            //所以执行处理客户端请求,我们让线程去干,这样就不会在一个线程内阻塞
//            Thread thread=new Thread(()->{
//                try {
//                    ProcessConnect(socket);
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//            });
//            thread.start();

            //线程池
            ExecutorService executorService= Executors.newCachedThreadPool();
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        ProcessConnect(socket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

    //给当前客户端提供服务方法
    public void ProcessConnect(Socket socket) throws IOException {
        //实际他们交流数据是一个为socket类型的文件
        System.out.printf("[地址:%s:端口号%d]建立连接成功\n",socket.getInetAddress().toString(),socket.getPort());
        try (InputStream inputStream = socket.getInputStream();//获取socket内部的input流
             OutputStream outputStream = socket.getOutputStream())//获取socket内部的output流
        {
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            //长连接写法
            while (true) {
                if(!scanner.hasNext()){
                    System.out.printf("[地址:%s:端口号%d]断开连接",socket.getInetAddress().toString(),socket.getPort());
                }
                //从客户端接收数据
                String resqust = scanner.next();
                System.out.println("服务器接收数据");

                //处理数据
                String response = process(resqust);
                System.out.println("服务器处理数据");

                //因为这用流接收 我们按下换行是被scanner接收但并未添加到数据内 而服务器接收流收到的数据内
                //是没有换行符的 就会一直阻塞 所以我们要在数据后再加个\n
                //服务器发送数据
                //printWriter.write(response+'\n');
                //或者直接使用println自带\n
                printWriter.println(response);
                System.out.println("服务器发送数据");

                //刷新缓冲区
                printWriter.flush();
                System.out.printf("[地址:%s:端口号:%d]接收数据:%s 响应数据:%s",socket.getInetAddress().toString(),socket.getPort(),resqust,response);
            }
        }
    }
    public String process(String s){
        return s;
    }

    public static void main(String[] args) throws IOException {
        Test1 t1=new Test1(8080);
        t1.start();
    }
}

客户端

1.创建Socket对象,并连接服务端的ip与端口号

    private Socket socket=null;

    public Test2() throws IOException {
        socket=new Socket("127.0.0.1",8080);

2.获取Socket流对象,写入数据,阻塞等待服务端响应

    public void start() throws IOException {
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream())
        {
            while (true){
                Scanner scanner=new Scanner(inputStream);
                PrintWriter printWriter=new PrintWriter(outputStream);

                Scanner scanner1=new Scanner(System.in);
                System.out.println("输入数据>");
                String requst=scanner1.next();
                //因为这用流接收 我们按下换行是被scanner接收但并未添加到数据内 而服务器接收流收到的数据内
                //是没有换行符的 就会一直阻塞 所以我们要在数据后再加个\n
                //printWriter.write(requst+'\n');
                //或者直接使用println自带\n
                printWriter.println(requst);
                //刷新缓冲区
                printWriter.flush();

                String response=scanner.next();
                System.out.println(response);
            }
        }finally {
            socket.close();
        }
    }

整体代码

package TestTCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

//客户端
public class Test2 {
    private Socket socket=null;

    public Test2() throws IOException {
        socket=new Socket("127.0.0.1",8080);
    }

    public void start() throws IOException {
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream())
        {
            while (true){
                Scanner scanner=new Scanner(inputStream);
                PrintWriter printWriter=new PrintWriter(outputStream);

                Scanner scanner1=new Scanner(System.in);
                System.out.println("输入数据>");
                String requst=scanner1.next();
                //因为这用流接收 我们按下换行是被scanner接收但并未添加到数据内 而服务器接收流收到的数据内
                //是没有换行符的 就会一直阻塞 所以我们要在数据后再加个\n
                //printWriter.write(requst+'\n');
                //或者直接使用println自带\n
                printWriter.println(requst);
                //刷新缓冲区
                printWriter.flush();

                String response=scanner.next();
                System.out.println(response);
            }
        }finally {
            socket.close();
        }
    }

    public static void main(String[] args) throws IOException {
        Test2 t2=new Test2();
        t2.start();
    }
}

jconsole用来监控java线程

jconsole在路径 jdk/bin/jconsole.exe

例:

我们需要看util最后一局,可以看出线程是在next阻塞着。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/426223.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot+vue的智能学习平台系统

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

2024年阿里云2核4G配置服务器测评_ECS和轻量性能测评

阿里云2核4G服务器多少钱一年?2核4G服务器1个月费用多少?2核4G服务器30元3个月、85元一年,轻量应用服务器2核4G4M带宽165元一年,企业用户2核4G5M带宽199元一年。本文阿里云服务器网整理的2核4G参加活动的主机是ECS经济型e实例和u1…

备战蓝桥杯---状态压缩DP基础2之TSP问题

先来一个题衔接一下: 与上一题的思路差不多,不过这里有几点需要注意: 1.因为某一列的状态还与上上一行有关,因此我们令f[i][j][k]表示第i行状态为j,第i-1行状态为k的最大炮兵数。 因此,我们可以得到状态转移方程&…

一篇文章吃透整个JVM,JVM超详细笔记

这里写目录标题 JVMJVM执行流程JVM执行流程 JVM内存模型1.堆区(Heap)2.虚拟机栈(JVM Stacks)3.本地方法栈(Native Method Stacks)4.程序计数器(Program Counter Register)5.方法区/元…

机器学习 -- Octave基本操作

场景 Octave语言是一种高级数值计算和数据可视化的开源软件。它提供了一种方便的方式来执行数值计算、数据分析和可视化,特别是在科学和工程领域中。今天学习了一下Octave的基本操作,记录一下。 下载 去Octave官网下载即可。octave下载可自行下载。 …

学习人工智能:Sora技术报告Video generation models as world simulators,2024.2

原文链接: Video generation models as world simulators (openai.com) 摘要: 我们探索了在视频数据上大规模训练生成模型。具体来说,我们在可变片长、分辨率和纵横比的视频和图像上联合训练文本条件扩散模型text-conditional diffusion mo…

案例介绍:信息抽取技术在汽车销售与分销策略中的应用与实践

一、引言 在当今竞争激烈的汽车制造业中,成功的销售策略、市场营销和分销网络的构建是确保品牌立足市场的关键。作为一名经验丰富的项目经理,我曾领导一个专注于汽车销售和分销的项目,该项目深入挖掘市场数据,运用先进的信息抽取…

【Mybatis】快速入门 基本使用 第一期

文章目录 Mybatis是什么?一、快速入门(基于Mybatis3方式)二、MyBatis基本使用2.1 向SQL语句传参2.1.1 mybatis日志输出配置2.1.2 #{}形式2.1.3 ${}形式 2.2 数据输入2.2.1 Mybatis总体机制概括2.2.2 概念说明2.2.3 单个简单类型参数2.2.4 实体…

租房招聘平台新篇章:Java+SpringBoot技术革新

✍✍计算机毕业编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java、…

【iOS ARKit】协作Session

使用 ARWorldMap,能解决使用者再次进入同一物理空间时的AR 场景恢复问题,也能在多人之间共桌AR 体验,但这种共享并不是实时的,在载入ARWorldMap 后,设备新检测到的环境信息和使用者所做操作不会实时共享,即…

Avalonia学习(二十八)-OpenGL

Avalonia已经继承了opengl,详细的大家可以自己查阅。Avalonia里面启用opengl继承OpenGlControlBase类就可以了。有三个方法。分别是初始化、绘制、释放。 这里把官方源码的例子扒出来给大家看一下。源码在我以前发布的单组件里面。地址在前面的界面总结博文里面。 …

使用 MinIO 对象存储,创建一个类似网盘的应用

Linux 的 MinIO 对象存储 MinIO 介绍 MinIO 是一个对象存储解决方案,提供与 Amazon Web Services S3 兼容的 API,并支持所有核心 S3 功能。MinIO 可以部署在任何地方。 Amazon Simple Storage Service (Amazon S3) 是一种对象存储服务,提供…

HarmonyOS云端一体化组件之AGC应用管理

(可选)在AGC控制台创建同包名应用 如创建工程时,发现尚未在AGC控制台创建与工程包名相同的应用,可进行补充创建。 1.点击界面提示内的“AppGallery Connect”,浏览器打开AGC控制台“我的项目”页面。 2.点击选择您希…

springboot239华府便利店信息管理系统

华府便利店信息管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本华府便利店信息管理系统就是在这样的大环境下诞生,其可以帮助管理者在…

非阻塞IO:提高应用程序的效率与性能

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

项目可行性方案:人脸识别实现无感考勤的项目技术可行性方案

目 录 1.引言 1.1编写目的 1.2背景 2.可行性研究的前提 2.1要求 2.2目标 3.对现有系统的分析 3.1系统改进示意图 3.2改进之处 3.3技术条件方面的可行性 4.结论 1.引言 1.1编写目的 本报告编写的目的是探究学校里对教室和办公室内教师的人脸进行识别从而…

【复现】宏景HCM 任意文件读取漏洞_63

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一: 四.修复建议: 五. 搜索语法: 六.免责声明 一.概述 宏景HCM 将人才标签技术应用于员工招聘、人才选拔等环节,通过多维度的标签体系,形成不同专业序列的人才画…

青少年CTF擂台挑战赛 2024 #Round 1 Web方向题解 WP 全

EasyMD5 题目描述:php没有难题 考点总结:脑洞题目,不如我出(狗头 只允许两个都上传pdf文件。 文件还不能太大了。burp多次发包发现要求两个pdf内容不一样 不一样时候,提示我们MD5碰撞。 科学计数法绕过 PHP的后门 …

《幻兽帕鲁》游戏对服务器性能的具体要求是什么?

《幻兽帕鲁》游戏对服务器性能的具体要求是什么? CPU:官方最低要求为i5-3570K,但在多人游玩时可能会有明显卡顿。此外,还有建议选择4核或更高性能的处理器,以确保游戏运行流畅。 内存:对于不同人数的联机&…

代码随想录day10(2)字符串:反转字符串Ⅱ (leetcode541)

题目要求:给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,则反转前…