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

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java可以通过TCP和UDP协议实现网络通信。其中,TCP协议提供面向连接的、可靠的数据传输服务,而UDP协议则提供无连接的、不可靠的数据传输服务。 下面是Java实现TCP网络通信的例子: ```java // 服务器端 ServerSocket serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String message = null; while ((message = br.readLine()) != null) { System.out.println("接收到客户端消息:" + message); } br.close(); socket.close(); serverSocket.close(); // 客户端 Socket socket = new Socket("127.0.0.1", 8888); OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(os); pw.write("Hello, server!"); pw.flush(); socket.shutdownOutput(); socket.close(); ``` 下面是Java实现UDP网络通信的例子: ```java // 服务器端 DatagramSocket serverSocket = new DatagramSocket(8888); byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); serverSocket.receive(packet); String message = new String(packet.getData(), 0, packet.getLength()); System.out.println("接收到客户端消息:" + message); serverSocket.close(); // 客户端 DatagramSocket clientSocket = new DatagramSocket(); String message = "Hello, server!"; byte[] buffer = message.getBytes(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("127.0.0.1"), 8888); clientSocket.send(packet); clientSocket.close(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值