IT技术之家

首页 > 服务器

服务器

JavavEE中网络编程Socket套接字Ⅱ(TCP)_会越来越强的哒哒

发布时间:2022-10-24 16:48:35 服务器 0次 标签:网络 tcp/ip 服务器 java 面试
JavavEE中网络编程Socket套接字Ⅱ(TCP)...

文章目录

TCP流套接字ServerSocket APISocket API🪁TcpEchoSever🪁TcpEchoClient 总结

TCP流套接字

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。
ServerSocket构造方法 :

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法:

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待(接受客户端的连接)
void close()关闭此套接字

Socket accept() 既会给服务器用,也会给客户端用~

Socket API

Socket 构造方法:

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

构造方法里面制定了IP和端口,构造方法就是在尝试和指定的服务器建立连接

Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址(对方的ip地址和端口)
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

通过 socket 可以获取到 两个流对象,分别用来读和写~~
TCP是面向字节流的,所以去读写 TCPsocket 本质上 和 读写文件 是一样的~

🪁TcpEchoSever

代码实现TCP 服务器:

package TCP;

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 TcpEchoSever {
    //代码中会设计多个 socket 对象,使用不同的名字来区分,使用listenSeverSocke来进行监听
    public ServerSocket listenSeverSocket = null;

    public TcpEchoSever(int port) throws IOException {
        listenSeverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true) {
            //1. 先调用 appect 来 接受客户端的连接
            Socket clientSocket = listenSeverSocket.accept();
            //2.处理这个连接
            //使用线程池保证可以同时处理多个客户端
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnect(clientSocket);
                }
            });
        }



    }

    public void processConnect(Socket clientSocket) throws IOException {
        System.out.println("客户端上线!");
        //接下来处理客户端的请求
        //拿到输入流输出流对象
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //客户端可能会发多条数据
            while(true) {
                //1.读取请求并解析
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) {
                    //读完了,连接断开
                    System.out.printf("客户端读完了:[%s:%d]",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //读取到请求
                String request = scanner.next();
                //2. 根据请求计算响应
                String response = process(request);
                //3.把响应写回给客户端
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                //刷新缓冲区确保数据确实是通过网卡发送出去了
                printWriter.flush();

                //打印日志
                System.out.printf("[客户端ip地址: %s 客户端端口号: %d],请求: %s , 回应: %s \n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            clientSocket.close();
        }
    }
    public String process(String requst) {
        return requst;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoSever tcpEchoSever = new TcpEchoSever(9000);
        tcpEchoSever.start();
    }
}


listenSeverSocketclientSocket 的区别:

?为什么要使用多线程?

如果不使用多线程:
如果没有客户端建立连接,服务器就会阻塞到accept~

如果有一个客户端过来了,此时就会进入到 processConnection方法~
但是连接建立了,如果客户端还没发消息,此时代码就会阻塞在hasNext

就无法第二次调用到accept,也就无法处理第二个客户端了!!

? 为什么要关闭 socket ? 而前面的 listenSocket 以及 udp 程序中的 socket 为什么就没有 close ?

socket也是一个文件
一个进程能够同时打开的文件个数,有上限!!! => PCB文件描述符表,不是无限的,要是满了就不能继续打开文件

listenSeverSocket 是在TCP服务器程序中只有唯一一个对象~ 就不会把文件描述符占满(随着进程的退出,自动释放)

clientSocket是在循环中,每个客户端连上来都要分配一个~
这个对象就会被反复的创建出实例,每创建一个,都要消耗一个 文件描述符~

因此就需要能够把不再使用的 clientSocket及时释放掉~

?为啥UDP没这个问题,而TCP有这个问题呢?

原因是TCP使用长连接这种连接方式~
UDP 客户端直接发消息即可~ 不必专注于处理某一个客户端~
TCP建立连接之后,要处理客户端的多次请求,才导致无法快速的调用到accept(长连接,导致专注于处理某一个客户端,务必要使用多线程)
如果TCP每个连接只处理一个客户端的请求,也能够保证快速调用到accept(短链接)

🪁TcpEchoClient

package TCP;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    public Socket socket = null;

    public TcpEchoClient(String severIP,int severPort) throws IOException {
        //和服务器建立连接,需要知道服务器在哪
        socket = new Socket(severIP,severPort);
    }

    public void start() throws FileNotFoundException {
        Scanner scanner = new Scanner(System.in);
        try(OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream()) {
            while (true) {
                //1.从控制台读取数据,构造成一个请求
                System.out.print("->");
                String requestStr = scanner.next();
                //2.将请求发给服务器
                PrintWriter requestPrintWriter = new PrintWriter(outputStream);
                requestPrintWriter.println(requestStr);
                //如果不flush,可能导致请求没有真发出去
                requestPrintWriter.flush();
                //3.从服务器读取Tcp响应数据
                Scanner response = new Scanner(inputStream);
                String responseStr = response.next();
                //4.把响应显示到界面上
                System.out.println(responseStr);
            }

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

    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9000);
        tcpEchoClient.start();
    }
}

?为什么Scanner 也不用关闭?

scannner这种关闭本质上是关闭了里面包含的InputStream,scanner自身是不会打开文件描述符的,而InputStream已经在try里面了,try运行完毕会自动关闭~

总结

你可以叫我哒哒呀
本篇到此结束
“莫愁千里路,自有到来风。”
我们顶峰相见!