TCP的socket API

1、核心类

ServerSocket :服务器使用的socket

Socket : 服务器和客户端都会使用的socket

accept进行的工作是拉客

对应操作系统来说,建立TCP连接是内核的工作

accept要干的就是等连接建立好了,把这个连接给拿到应用程序中。

如果当前连接还没建立,accept就会阻塞,accept相当于,有人给你打电话了,你按下接听键!如果没人打电话,就阻塞等待。

一、回显服务器

 1、服务器

注:

1、代码中,使用了 clientSocket 用完之后,需要关闭。

和代码中 ServerSocket 生命周期不一样,ServerSocket的生命周期跟随整个程序, clientSocket生命周期,只是当前连接。就应该在连接之后,把这里的 socket 关闭,不关就会造成资源泄露。ServerSocket只有一个,clientSocket 会有无数个,每个客户端的连接,都是一个。

2、利用多线程,来实现多个客户端同时运行

一个线程调用一个processConnect。

3、通过线程池,来解决频繁创建销毁线程的问题

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.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        //创建线程池
        ExecutorService service = Executors.newCachedThreadPool();
        while (true){
            Socket clientSocket = serverSocket.accept();

            //[版本1] 单线程版本,存在bug,无法处理多个客户端
            //不是直接调用,而是创建一个新的线程,让新的线程来调用。
            //processConnect(clientSocket);

            //[版本2] 多线程版本,主线程负责拉客,新线程负责通信
            //虽然比版本1有提升,但是涉及到频繁创建销毁线程,在高并发的情况下,负担是比较重的
//            Thread T = new Thread(() -> {
//                try {
//                    processConnect(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            T.start();

            //[版本3] 使用线程池,来解决频繁创建销毁线程的问题
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnect(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    //通过这个方法,给当前连上的这个客户端,提供服务
    //一个连接过来了,服务方式可能有两种:
    //1、一个连接只进行一次数据交互(一个请求+一个响应) 短连接
    //2、一个连接进行多次数据交互(N个请求 + N个响应)  长连接
    //此处写长连接的版本,短连接就是长连接去掉while循环
    public void processConnect(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 建立连接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            //这里是长连接的写法,需要while来获取多次交互情况
            while (true){
                if(!scanner.hasNext()){
                    //读完了,就断开连接。即当客户端断开连接的时候,此时 hasnext 就会返回 false
                    //读到EOF的时候(end of file)返回false,当客户端进程退出的时候,也就会触发socket文件关闭,此时服务器这边尝试读取请求,就会读到EOF
                    System.out.printf("[%s:%d] 断开连接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                   break;
                }
                //1、读取请求并解析
                String request = scanner.next();
                //2、根据请求计算响应
                String response = process(request);
                //3、把响应写回给客户端
                printWriter.println(response);
                //4、刷新一下缓冲区,避免数据没有真的发出去
                printWriter.flush();
                System.out.printf("[%s:%d] req:%s;resp:%s\n",
                        clientSocket.getInetAddress().toString(),clientSocket.getPort(),
                        request,response);
            }
        }finally {
            clientSocket.close();
        }
    }
    public String process(String req){
        return req;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer echoServer = new TcpEchoServer(8000);
        echoServer.start();
    }
}

2、客户端

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;

public class TcpEchoClient {
   private Socket socket = null;
   public TcpEchoClient() throws IOException {
      //new这个对象的时候,需要和服务器建立连接 建立连接,就得知道,服务器在哪里
      //new Socket的过程就是建立连接的过程
      socket = new Socket("127.0.0.1",8000);
   }
   public  void start() throws IOException {
      //由于实现的是长连接,一个连接会处理 N 个请求和响应,需要加上while循环
      //从键盘输入
      Scanner scanner = new Scanner(System.in);
      try(InputStream inputStream = socket.getInputStream();
          OutputStream outputStream = socket.getOutputStream()) {
         //从网络上输入
         Scanner scannerNet = new Scanner(inputStream);
         PrintWriter printWriter = new PrintWriter(outputStream);
         while(true){
            //1、从控制台读取用户输入
            System.out.print("> ");
            String request = scanner.next();
            //2、把请求发送给服务器
            printWriter.println(request);
            printWriter.flush();
            //3、从服务器读取响应
            String response = scannerNet.next();
            //4、把结果显示到界面上
            System.out.printf("req:%s resp:%s\n",request,response);
         }
      }

   }

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

二、翻译服务器

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class TcpDictServer extends TcpEchoServer{
    private Map<String,String> dict = new HashMap<>();

    public TcpDictServer(int port) throws IOException {
        super(port);
        dict.put("cat","小猫");
        dict.put("dog","小狗");
        dict.put("宝贝","瑾瑾");

    }
    @Override
    public String process(String req){
        return dict.getOrDefault(req,"俺也不知道");
    }

    public static void main(String[] args) throws IOException {
        TcpDictServer server = new TcpDictServer(8000);
        server.start();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值