[TCP协议]基于TCP协议的字典服务器

目录

1.TCP协议简介:

2.TCP协议在Java中封装的类以及方法

3.字典服务器

3.1服务器代码:

3.2客户端代码:


1.TCP协议简介:

TCP协议是一种有连接,面向字节流,全双工,可靠的网络通信协议.它相对于UDP协议来说有以下几点好处:

1.它是可靠传输,相比于UDP协议,传输的数据更加可靠.当然这里的可靠是相对的,并不是真的万无一失.我们会在后面的博客中给大家详细介绍TCP协议的可靠传输.

2.它传输的字节流文件没有大小限制,不像UDP协议,一次只能传输64kb的报文数据.

但是也有缺点:

1.开销大,需要对连接的建立与维护,以及确认数据报的确认和重传,会增加网络开销.

2 传输效率低;由于可靠性和流量控制,会对传输效率造成一定的影响

基于这些特性:因此TCP协议不适用于实时性要求高的程序,适合数据库访问,网络游览器,文件传输,电子邮件等应用.

2.TCP协议在Java中封装的类以及方法

TCP协议在Java中有两个api,分别是SeverSocket和Socket

顾名思义,SeverSocket是给服务器用的api,它仅限于服务器使用,而Socket服务器和客户端都可以使用.

SeverSocket构造方法:

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

SeverSocket的方法:

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

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket的构造方法:

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

Socket的方法:

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

3.字典服务器

下面我们基于这两个api做一个简单的字典服务器.它类似于翻译软件,当我们去输入中文字符的时候,会返回并且打印与之对应的英文字符.

为了更方便大家理解,我们先做一个在之前的文章提过的回显服务器:

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

public class TcpEchoSever {
    ServerSocket serverSocket = null;
    public TcpEchoSever(int port) throws IOException { //指定服务器的端口号
        serverSocket = new ServerSocket(port);
    }
    public void strat() throws IOException {
        System.out.println("服务器启动");
        while (true){
            Socket socket = serverSocket.accept(); //通过acccept方法获取到我们的客户端信息并保存到socket中
            Thread t = new Thread(()->{  //这里使用多线程是为了让不同的客户端都分配一个线程,以免因为一个客户端让它阻塞而别的客户端不能访问
                try {
                    accress(socket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            t.start();

        }
    }

    public void accress(Socket socket) throws IOException {
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) { //字节流,有点类似于文件操作
            System.out.printf("[客户端连接%s:%d]\n",socket.getInetAddress(),socket.getPort());
            while (true) {
                {   Scanner inputScnner = new Scanner(inputStream);
                    if (!inputScnner.hasNext()) {
                        System.out.println("连接中断");
                        break;
                    }
                    String request = inputScnner.next(); //读取数据
                    String reqonse = func(request); //把数据进行服务器端的计算请求
                    PrintWriter writer  = new PrintWriter(outputStream);//将响应返回给客户端
                    writer.println(reqonse);
                    writer.flush();//这里的flush是为了把缓冲池里的数据给刷新
                    System.out.printf("[%s:%d]\n,request : %s ,reqonse : %s"
                    ,socket.getInetAddress(),socket.getPort(),request,reqonse);//打印日志
                }
            }
        }finally {
            socket.close();//防止客户端过多,造成文件资源泄露,让文件描述符表满了
        }
    }
    public String func(String request){
        return request;
    }

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

我们直接继承这个服务器代码,并且重写func方法,将我们的字典加入进去

3.1服务器代码:

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

public class TcpDcarySever extends TcpEchoSever{
    HashMap hashMap = new HashMap<>();

    public TcpDcarySever(int port) throws IOException {
        super(port);
        hashMap.put("小狗","dog");
        hashMap.put("小猫","cat");
        hashMap.put("小坤","chincken");
    }
    @Override
    public String func(String request) {
        return hashMap.getOrDefault(request,"没找到,请重新查找");
    } //重写func方法
    public static void main(String[] args) throws IOException {
        TcpDcarySever tcpDcarySever = new TcpDcarySever(9091);
        tcpDcarySever.strat();
    }
}

3.2客户端代码:

步骤都在注释里

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 TcpEchoClient {
    Socket socket = null;
    public TcpEchoClient(String SeverIp,int port) throws IOException { //体现了TCP协议的有连接 得在构造方法中把目的服务器的IP地址和端口号指定
        socket = new Socket(SeverIp , port);
    }
    private void start(){
        System.out.println("客户端启动");
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){ //一样是面向字节流 ,所以和文件操作类似
            Scanner scanner = new Scanner(System.in);
            Scanner inpuscnner = new Scanner(inputStream);
            PrintWriter writer = new PrintWriter(outputStream); 
            while (true){
                if(!scanner.hasNext()){
                    break;
                }
                String requeset = scanner.next();//从控制台读取数据
                writer.println(requeset);//发送请求
                writer.flush();//刷新缓冲池
                String reqonse = inpuscnner.next();//从服务器端拿到请求并构造成字符串
                System.out.println(reqonse);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9091);//指定目标的IP地址和端口号
        tcpEchoClient.start();
    }
}

结果演示:

启动服务器:

[TCP协议]基于TCP协议的字典服务器_第1张图片

启动客户端观察服务器的情况:

[TCP协议]基于TCP协议的字典服务器_第2张图片

[TCP协议]基于TCP协议的字典服务器_第3张图片

输入中文字符看看:

[TCP协议]基于TCP协议的字典服务器_第4张图片

此时客户端是这样的

[TCP协议]基于TCP协议的字典服务器_第5张图片

为了观察多线程的作用,我们再次启动另一个客户端:
[TCP协议]基于TCP协议的字典服务器_第6张图片

可以看到另一个客户端也脸上了

你可能感兴趣的:(tcp/ip,服务器,网络)