基于TCP的简单服务器

基于Tcp的简单服务器
  • UDP协议无连接类似于发微信
  • TCP协议有链接类似于打电话
  • 所以基于TCP的服务器先要到内核中获取一个TCP连接,UDP不需要获取连接
  • TCP的连接管理是由操作系统的内核来管理的,客户端和服务器建立连接的过程中,完全由内核来进行负责。负责程序的代码感知不到
  • TCP的来连接管理需要先描述在组织
    • 描述:通信的五元组
    • 组织:使用一个阻塞队列来组织若干个连接对象
  • 代码中调用这个accept方法就是从阻塞队列当中获取一个链接对象(在应用程序中的化身就是Socket对象)
  • 后续数据读写都是根据这个clientSocket这个对象来进行展开的
  • 如果都服务器启动之后,没有客户端建立连接,此时代码中调用accpt方法就会阻塞,阻塞到真的有客户端建立连接为止。
  • 简单的TCP服务器
package com.zb.network.test;

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

/**
 * @Description: 基于Tcp的简单服务器
 * @Author:啵啵啵啵啵啵唧~~~
 * @Date:2022/5/4
 */
public class TcpEchoServer {
    /**
     * 1、初始化服务器
     * 2、进入主循环
     *  1.先去从内核当中获取到一个TCP连接
     *  2.处理这个TCP连接
     *   a.读取请求并解析
     *   b.根据请求计算响应
     *   c.把响应写回给客户端
     */

    /**
     * 创建于给serverSocket对象
     */
    private ServerSocket serverSocket = null;

    /**
     * 构造方法,初始化serverSocket对象
     *
     * @param port
     * @throws IOException
     */
    public TcpEchoServer(int port) throws IOException {
        //这个操作和UDP类似需要绑定端口号
        serverSocket = new ServerSocket(port);
    }

    /**
     * 启动服务器
     *
     * @throws IOException
     */
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true) {
            //1.先从内核当中获取一个TCP连接,当客户端没有连接过来的时候,accept就会陷入阻塞
            Socket clientSocket = serverSocket.accept();
            //2.处理这个链接
            processConnection(clientSocket);
        }

    }

    /**
     * 处理连接的方法
     * 一个连接中客户端和服务器之交互一次么?
     * 服务器的处理方式有两种
     * 1.一个连接中客户端和服务器只交互一次,交互完毕,就断开连接(短连接)
     * 2.一个连接中,客户端和服务器之间交互N次,直到满足一定的条件再断开连接(长连接)
     * 其中长连接的效率小队来说效率高一些
     *
     * @param clientSocket
     */
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d]客户端上线\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        //通过clientSocket来和客户端交互,先做好准备工作,获取到clientSocket中的流对象
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))){
            //此处实现一个长连接的版本,一次处理的过程当中,需要处理多个请求和响应
            //当客户端断开连接的时候,服务器再去调用readLine或者write方法都会触发异常(IOException)
            while (true) {
                //1.读取请求并解析
                //此处暗含一个重要的信息,客户端发的数据必须是一个按行发送的数据(每条数据一行)
                String request = bufferedReader.readLine();

                //2.根据请求计算响应
                String response = process(request);

                //3.把响应写回到客户端(客户端按行来读)
                bufferedWriter.write(response + "\n");
                //手动刷新缓冲区
                bufferedWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            System.out.printf("[%s:%d]客户端下线\n", clientSocket.getInetAddress().toString(),
                    clientSocket.getPort());
        }
    }

    /**
     * 根据请求计算响应
     *
     * @param request
     * @return
     */
    private String process(String request) {
        return request;
    }

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

在这里插入图片描述

  • TCP客户端
package com.zb.network.test;

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

/**
 * @Description:
 * @Author:啵啵啵啵啵啵唧~~~
 * @Date:2022/5/4
 */
public class TcpEchoClient {
    //1.启动客户端(一定不要绑定端口号)和服务器建立连接
    //2.进入主循环
    // a.读取用户输入的内容
    // b.构造一个请求发送给服务器
    // c.读取服务器响应的数据
    // d.把响应数据显示到界面上

    private Socket socket = null;

    /**
     * 实例化socket
     * @param serverIp
     * @param serverPort
     * @throws IOException
     */
    public TcpEchoClient(String serverIp, int serverPort) throws IOException {
        //此处实例化就是建立TCP连接(三次握手的过程)
        socket = new Socket(serverIp,serverPort);
    }


    public void start(){
        System.out.println("客户端启动");
        Scanner sc = new Scanner(System.in);
        try(  //获取两个流对象
              BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
              BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
            while (true){
                //读取用户输入的内容
                System.out.print("->");
                String request = sc.nextLine();
                if ("exit".equals(request)){
                    break;
                }
                //2.客户端构造请求并发送.此处+\n为了和服务器中的readLine向对应
                bufferedWriter.write(request + "\n");
                //刷新缓存
                bufferedWriter.flush();
                //3.客户端读取响应数据。
                String response = bufferedReader.readLine();

                //4.把响应数据显示到界面上.
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

}

在这里插入图片描述

注意
  • UDP中必须用receive和send.传输数据的基本单位是DatagramPacket对象.[面向数据报]
  • TCP中必须用read和write,传输数据的基本单位是字节.[面向字节流 ]
  • 他们传输协议上的差异导致代码的差异
  • Tcp要刷新缓冲区的作用
  • //--------------------服务器刷新缓冲区-----------------------
     //3.把响应写回到客户端(客户端按行来读)
                    bufferedWriter.write(response + "\n");
                    //手动刷新缓冲区
                    bufferedWriter.flush();
    //----------------------客户端刷新缓冲区----------------------
                    //2.客户端构造请求并发送.此处+\n为了和服务器中的readLine向对应
                    bufferedWriter.write(request + "\n");
                    //刷新缓存
                    bufferedWriter.flush();
    
    在这里插入图片描述
解决Tcp连接只能有一个客户端连接服务器的问题
  • 原因就是accept方法的调用,调用几个accept方法就处理几个连接,在代码中出现的问题就是由于为了要实现长连接,导致循环退不出去,导致accept方法无法正常调用,所以只能有一个客户端和服务器进行连接
  • 解决方法就是使用多线程,用一个线程专门来调用accept方法从而实现并发操作,这样既可以实现长连接又可以正常调用accept方法,其实单线程的方式也可以解决,使用IO多路复用即可,而且十分高效,但是比较麻烦
    在这里插入图片描述
  • 这样就可以让多个客户端同时连接服务器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值