当前位置:   article > 正文

基于TCP协议实现回显服务器和客户端_按照tcp连接构建服务端,要求能返回信息及回传数据

按照tcp连接构建服务端,要求能返回信息及回传数据

前言

关于回显服务器的概念在上篇博客中有解释

一、TCP和UDP实现的回显服务器有什么区别?

(1)TCP是面向字节流的,UDP是面向数据报的;
(2)TCP的服务器需要与操作系统内核建立连接,UDP不需要
(3)TCP是可靠传输,UDP是不可靠传输
(4)UDP效率较高,TCP相对UDP来说效率稍微逊;

二、如何构建基于TCP协议的回显服务器

1.实例化serverSocket对象

代码如下:

public class TCPEchoServer {
    private ServerSocket serverSocket = null;

    //初始化serverSocket时需要绑定端口号
    public TCPEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.进入主循环

	进入主循环需要完成的事情:
	 - 与TCP建立连接
	 - 处理TCP连接
		 - 获取请求并解析
		 - 根据请求计算响应
		 - 将响应写回客户端
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

代码如下:

public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            //负责与客户端交互
            //与TCP建立连接
            Socket clientSocket = serverSocket.accept();
            //处理连接
            processConnection(clientSocket);
        }


    }

    private void processConnection(Socket clientSocket) {
        System.out.printf("客户端上线 [%s:%d]",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        //TCP是面向字节流的,由于是回显服务器主要针对字符流文件,因此这里需要将字节流文件字符流化
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
        ){
            while(true){
                //获取请求并解析
                //注意此处readLine()规定了读取是按照行读取的,同样写数据必须按行写,相当于自定义协议
                String request = bufferedReader.readLine();
                //根据请求计算响应
                String response = process(request);
                //将响应写回客户端
                //这里之所以加上换行符的原因是因为要按行写数据,需要遵循自定义协议
                bufferedWriter.write(response+"\n");
                //由于 bufferedWriter是带有缓冲区的,没有刷新的话数据就在缓冲区中,没有真正写到socket中
                //需要手动刷新
                bufferedWriter.flush();
                //打印日志
                System.out.printf("[%s:%d] req:%s resp:%s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }

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

    private String process(String request) {
        return request;
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

完整代码如下:

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

/**
 * Author:ZouDouble
 * Description:
 * 天气:晴天
 * 目标:Good Offer
 * Date    2021-01-05 17:20
 */
public class TCPEchoServer {
    private ServerSocket serverSocket = null;

    //初始化serverSocket时需要绑定端口号
    //serverSocket负责与客户端进行连接
    public TCPEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            //负责与客户端交互
            //与TCP建立连接
            Socket clientSocket = serverSocket.accept();
            //处理连接
            processConnection(clientSocket);
        }


    }

    private void processConnection(Socket clientSocket) {
        System.out.printf("客户端上线 [%s:%d]",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        //TCP是面向字节流的,由于是回显服务器主要针对字符流文件,因此这里需要将字节流文件字符流化
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
        ){
            while(true){
                //获取请求并解析
                //注意此处readLine()规定了读取是按照行读取的,同样写数据必须按行写,相当于自定义协议
                String request = bufferedReader.readLine();
                //根据请求计算响应
                String response = process(request);
                //将响应写回客户端
                //这里之所以加上换行符的原因是因为要按行写数据,需要遵循自定义协议
                bufferedWriter.write(response+"\n");
                //由于 bufferedWriter是带有缓冲区的,没有刷新的话数据就在缓冲区中,没有真正写到socket中
                //需要手动刷新
                bufferedWriter.flush();
                //打印日志
                System.out.printf("[%s:%d] req:%s resp:%s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }

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

    private String process(String request) {
        return request;
    }

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

三、如何构建基于TCP协议的回显服务器的客户端

1.实例化Socket对象

代码如下:

public class TCPEchoClientServer {
    private Socket socket = null;
    //注意:这里用的是Socket类,这里的serverIp和serverPort仅仅只是属性,初始化的时候并没有绑定端口号
    //客户端初始化的时候不需要绑定端口号
    public TCPEchoClientServer(String serverIp,int serverPort) throws IOException {
        socket = new Socket(serverIp,serverPort);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.进入主循环

	进入主循环需要完成的事情:
		 - 读取客户请求并解析
		 - 构造请求发给服务器
		 - 从服务器读取响应
		 - 将响应写回客户端
  • 1
  • 2
  • 3
  • 4
  • 5

代码如下:

 public void start(){
        System.out.println("客户端来咯");
        Scanner scanner = 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.println("输入请求->");
                String request = scanner.nextLine();
                if ("exit".equals(request)){
                    System.out.println("退出");
                    break;
                }
                //构造请求数据
                bufferedWriter.write(request+"\n");
                bufferedWriter.flush();
                //从服务器读取响应
                String response = bufferedReader.readLine();
                //将响应写回客户端
                System.out.println(response);

            }

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

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

完整代码如下:

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

/**
 * Author:ZouDouble
 * Description:
 * 天气:晴天
 * 目标:Good Offer
 * Date    2021-01-05 17:42
 */
public class TCPEchoClientServer {
    private Socket socket = null;
    //注意:这里用的是Socket类,这里的serverIp和serverPort仅仅只是属性,初始化的时候并没有绑定端口号
    //客户端初始化的时候不需要绑定端口号
    public TCPEchoClientServer(String serverIp,int serverPort) throws IOException {
        socket = new Socket(serverIp,serverPort);
    }
    public void start(){
        System.out.println("客户端来咯");
        Scanner scanner = 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.println("输入请求->");
                String request = scanner.nextLine();
                if ("exit".equals(request)){
                    System.out.println("退出");
                    break;
                }
                //构造请求数据
                bufferedWriter.write(request+"\n");
                bufferedWriter.flush();
                //从服务器读取响应
                String response = bufferedReader.readLine();
                //将响应写回客户端
                System.out.println(response);

            }

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

    }

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

运行截图:
在这里插入图片描述
在这里插入图片描述

这样写出的代码有一个bug,它的建立连接和处理连接是串行执行的,代码中的while()循环是客户端下线才会触发,因此这个代码的局限性就是仅支持单用户使用,改进方法就是让它并发执行!如果使用多线程的话,由于多线程中也涉及到线程的创建与销毁,也是一些开销~最直接一步到位的方法就是利用线程池。接下来,我把多线程版本的和线程池版本的代码都放在下面,有需要的朋友可以看看!

基于多线程实现

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

/**
 * Author:ZouDouble
 * Description:
 * 天气:晴天
 * 目标:Good Offer
 * Date    2021-01-05 18:02
 */
public class TCPThreadEchoServer {
    private ServerSocket serverSocket = null;

    public TCPThreadEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            Socket clientSocket = serverSocket.accept();
            Thread thread = new Thread(){
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            };
            thread.start();
        }
    }
    private void processConnection(Socket clientSocket){
        System.out.printf("[%s:%d]客户端上线~\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
        ) {
            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());
        }
    }

    private String process(String request) {
        return request;
    }

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

基于线程池实现

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Author:ZouDouble
 * Description:
 * 天气:晴天
 * 目标:Good Offer
 * Date    2021-01-05 19:58
 */
public class TCPEThreadPoolEchoServer {
    private ServerSocket serverSocket = null;

    public TCPEThreadPoolEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService executorService = Executors.newCachedThreadPool();
        while(true){
            Socket clientSocket = serverSocket.accept();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }
    private void processConnection(Socket clientSocket){
        System.out.printf("客户端上线[%s:%d]",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
        ) {
            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]",clientSocket.getInetAddress().toString(),
                    clientSocket.getPort());
        }
    }
    private String process(String request){
        return request;
    }

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号