Java网络通信TCP

news2024/12/23 12:36:06

目录

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阻塞着。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp 部署h5,pdf预览

1.hubuilderx 打包h5。 2.上传部署包到服务器。 解压部署包:unzip h5.zip 。 3.nginx配置。 user root; worker_processes 1; #worker_cpu_affinity 0001 0010 0100 1000; #error_log logs/error.log; #error_log logs/error.log notice; error_log /var/l…

C#/.NET/.NET Core优秀项目和框架2024年2月简报

前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学…

Java基础 - 6 - 面向对象(二)

Java基础 - 6 - 面向对象(一)-CSDN博客 二. 面向对象高级 2.1 static static叫做静态,可以修饰成员变量、成员方法 2.1.1 static修饰成员变量 成员变量按照有无static修饰,分为两种:类变量、实例变量(对象…

项目设计:基于Qt和百度AI的车牌识别系统(嵌入式ARM)

基于Qt和百度AI智能云实现的智能车牌识别系统,具体可实现为停车场管理系统、智能计费停车系统…等。 1.系统实现思路及框架 1.1实现思路 要实现一个车牌识别系统,有多种方法,例如用opencv图像算法实现,或用第三方算法接口&#x…

【每日一题】找到字符串中所有字母异位词

目录 题目:思路:暴力枚举:滑动窗口: 代码实现:一些优化:代码实现: 题目: 找到字符串中所有字母异位词 思路: 暴力枚举: 对于有关子串的题目我们使用暴力枚…

H12-821_113

113.如图所示是路由器现ATE输出的部分信息,以下关于这部分信息的描述,错误的是哪一项? A.display pim rp-info命令用来查看组播组对应的RP信息 B.RP地址是2.2.2.2 C.组地址是225.0.0.0 D.RP的优先级是0 答案:C 注释: …

C语言-柔性数组成员的使用

文章目录 摘要柔性数组成员基本使用细节探究 零长度数组-定长数组-变长数组 摘要 本文先介绍柔性数组成员(flexible array member)的基本使用,然后介绍其内存结构。最后,补充了一些数组相关的其他概念。 柔性数组成员 基本使用 参考: 【C语言内功修炼…

有趣的CSS - 一串乱码

大家好,我是 Just,这里是「设计师工作日常」,今天分享的是通过 css 来实现一段不停变化的 bug 乱码效果。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面…

AMDGPU KFD Test 编译使用

ROCT-Thunk-Interface是一个用于在ROCm软件堆栈中提供设备无关性的层。它是ROCm的一部分,允许不同的硬件平台(如AMD GPU和Intel CPU)使用相同的API进行计算。 要安装ROCT-Thunk-Interface,首先需要创建一个新的目录,并…

开源免费的文件互传工具,LocalSend软件推荐

怎么把手机里的文件传到电脑或电脑的文件传到手机? 在日常生活和工作中,我们经常需要在不同的设备间传输文件,比如照片、视频、文档等。如果你使用是统一的苹果设备Airdrop可以非常方便的共享文件,但是如果你要在不同操作系统的设…

css 鼠标移入放大的效果

效果 HTML <div class"img-wrap"><img class"img-item" src"../assets/1.png" alt"" srcset""></div> CSS <style lang"less" scoped> .img-wrap {/* 超出隐藏 */overflow: hidden;.img-…

开发环境热部署

为什么需要热部署 在实际开发中&#xff0c;经常要修改代码&#xff0c;然后重启服务&#xff0c;再验证代码是否生效。对于开发场景&#xff0c;随着项目的演进&#xff0c;微服务越来越多&#xff0c;等待重启的时间也会越来越多&#xff1b;对于联调场景&#xff0c;对一处…

代码随想录Day66 | 图的DFS与BFS

代码随想录Day66 | 图的DFS与BFS DFS797.所有可能的路径无向图和有向图的处理 BFS200.岛屿数量 DFS 文档讲解&#xff1a;代码随想录 视频讲解&#xff1a; 状态 本质上就是回溯算法。 void dfs(参数) {if (终止条件) {存放结果;return;}for (选择&#xff1a;本节点所连接的…

for循环语句

语法格式&#xff1a; for (表达式1; 表达式2; 表达式3) { 若干语句;//循环体 } 执行规则 STEP1&#xff1a;计算“表达式1” &#xff0c;初始化。 STEP2&#xff1a; 判断“表达式2”的值&#xff0c;若 “表达式2”的值为true&#xff0c;则进行 STEP2&#xff0c;否则进行…

DevStack 基于 Ubuntu 部署 OpenStack

Devstack 简介 DevStack 是一系列可扩展的脚本&#xff0c;用于基于 git master 的最新版本快速调出完整的 OpenStack 环境。devstack 以交互方式用作开发环境和 OpenStack 项目大部分功能测试的基础。 devstack 透过执行 stack.sh 脚本&#xff0c;搭建 openstack 环境&…

ChatGPT最新功能“Text To Speech (TTS,文本转语音)”详细解读!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

32单片机基础:PWM驱动舵机,直流电机

PWM驱动舵机 接线图如上图所示。注意&#xff0c;舵机的5V 线不能接到面包板上的正极&#xff0c;面包板上的正极只有3.3V,是STM32提供的&#xff0c;所以要接到STLINK的5V, 我们如何驱动舵机呢&#xff1f;由之前我们介绍原理知道&#xff0c;要输出如下图对应的PWM波形才行…

【FPGA/IC】CRC电路的Verilog实现

前言 在通信过程中由于存在各种各样的干扰因素&#xff0c;可能会导致发送的信息与接收的信息不一致&#xff0c;比如发送数据为 1010_1010&#xff0c;传输过程中由于某些干扰&#xff0c;导致接收方接收的数据却成了0110_1010。为了保证数据传输的正确性&#xff0c;工程师们…

CentOS配网报错:network is unreachable

常用命令&#xff1a; 打开&#xff1a; cd /etc/sysconfig/network-scripts/ 修改&#xff1a; vim ifcfg-ens33 打开修改&#xff1a; vim /etc/sysconfig/network-scripts/ifcfg-ens33 保存&#xff1a; 方法1&#xff1a;ESCZZ&#xff08;Z要大写&#xff09; 方…

递归实现n的k次方(C语言)

编写一个函数实现n的k次方&#xff0c;使用递归实现。 下面来说一下思路 5的3次方&#xff1a;就是5*(5的3-1次方) 7的4次方&#xff1a;就是7*&#xff08;7的4-1次方&#xff09; 以此类推 n的k次方就是&#xff1a;n* n的&#xff08;k-1&#xff09;次方 int Func(int n,…