TCP协议常用API以及实现TCP客户端服务端

目录

TCP常用API

ServerSocket

Socket

TCP服务端(单线程)

属性+构造方法:

代码编写

TCP客户端(单线程)

属性+构造方法

代码编写

单线程TCP客户端---服务端通信结果

单线程TCP存在的问题

TCP服务端(支持多个客户发送请求)

多线程版本服务端

线程池版本服务端

关于TCP的长连接和短连接

短连接工作过程

长连接工作过程


TCP常用API

ServerSocket:专门给服务端使用的socket。

**Socket:**既可以提供给客户端使用,也可以给服务端使用。

ServerSocket

构造方法:

|------------------------|------------------------------|
| 方法签名 | 方法说明 |
| ServerSocket(int port) | 创建一个服务端嵌套字,并且指定服务端所占用的进程 |

成员方法 accept:

方法签名 方法说明
Socket accept() TCP是"有连接"的协议,TCP客户端与服务端一定要建立连接,才可以互相发送消息。因此这个accept方法,返回的socket对象,服务端就是通过这个socket对象和客户端进行通信的。 如果服务端没有收到socket对象,那么就会阻塞等待,无法进行通信。

Socket

对于服务端来说,是由accept()方法返回的的,返回的socket对象用于和客户端进行通信。

构造方法 :

对客户端来说,构造方法构造对象时需要指定ip地址和端口号,这个ip和端口号是服务端的

两个普通常用的方法:

|-------------------|------------------------|
| 方法签名 | 方法说明 |
| getInputStream() | 通过socket对象,获取到内部的输入流对象 |
| getOutputStream() | 通过socket对象,获取到内部的输出流对象 |

TCP服务端(单线程)

属性+构造方法:

需要在TcpEchoServer内部封装一个属性,这个属性是ServerSocket。

在构造方法当中,需要指定ServerSocket占用哪个端口号,此端口号就是服务端的端口号

代码编写

java 复制代码
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 TcpEchoServer {
    /**
     * 用于TCP客户端和服务端通信的socket对象
     */
    private ServerSocket serverSocket;

    /**
     * 构造方法指定服务端所占用的端口号
     */
    public TcpEchoServer(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }
    /**
     * 启动服务端
     */
    public void start() throws IOException {
        System.out.println("启动服务端!");
        while(true){
            //使用clientSocket与客户端交流
            Socket clientSocket=serverSocket.accept();//1、接收客户端发送的socket
            processConnection(clientSocket);//2、处理客户端的连接
        }
    }

    /**
     * 处理客户端发送来的连接
     * 客户端发送来的连接:clientSocket
     */
    public void processConnection(Socket clientSocket) throws IOException {

        //输出客户端ip地址和端口号
        System.out.println("客户端已上线!客户端的IP是:"+clientSocket.getInetAddress()+
                "客户端的端口号是:"+clientSocket.getPort());
        //2-1、读取clientSocket中的输入输出流对象
        InputStream inputStream=clientSocket.getInputStream();
        OutputStream outputStream=clientSocket.getOutputStream();
        //使用while循环处理多个请求和响应
        while (true){
            //2-2、通过Scanner来获取输入流
            Scanner scanner=new Scanner(inputStream);
            //读取完毕直接返回
            if(!scanner.hasNext()){
                System.out.println("客户端已经下线!客户端的IP是:"
                        +clientSocket.getInetAddress()+
                        ";客户端的端口是:"+clientSocket.getPort());
                //退出循环
                break;
            }
            //2-3、根据请求计算响应
            String request=scanner.next();
            //构造回写内容
            String response="服务器已经响应:"+request;
            //2-4、使用PrintWriter发送OutputStream
            PrintWriter printWriter=new PrintWriter(outputStream);
            printWriter.println(response);
            //刷新缓冲区保证能够发送出去
            printWriter.flush();
        }
        //2-5、关闭连接
        clientSocket.close();

    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);
        tcpEchoServer.start();//服务端启动
    }
}

TCP客户端(单线程)

属性+构造方法

需要在TcpEchoClient中加入Socket指定服务端的Ip地址和端口号

java 复制代码
import java.io.IOException;
import java.net.Socket;

public class TcpEchoClient {
    //客户端属性socket指定服务端IP+端口号
    private Socket socket;
    public TcpEchoClient(String serverIp,int port) throws IOException {
        socket=new Socket(serverIp,port);
    }
    
}

TCP是有连接的,所以要想使客户端和服务端可以通信就需要先建立连接,这里的socket对象创建时就说明客户端和服务端成功建立了连接。

客户端的socket创建完成的瞬间,服务端的accept方法就会立即接收到客户端的socket对象。

代码编写

java 复制代码
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指定服务端IP+端口号
    private Socket socket;
    public TcpEchoClient(String serverIp,int port) throws IOException {
        socket=new Socket(serverIp,port);
    }
    //启动客户端
    public void start() throws IOException {
        System.out.println("客户端已上线!");
        //1、利用socket获取到与服务端进行数据交互的输入输出流(inputStream和outputStream)
        Scanner input=new Scanner(System.in);
        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream=socket.getOutputStream();//都是相对于客户端来进行输入/输出操作的。
        while(true){
            //2、从控制台获取用户输入的信息
            System.out.println("输入你想要发送的信息:");
            String request=input.next();
            //3、将获取到的request通过流的方式发送给服务端
            PrintWriter printWriter=new PrintWriter(outputStream);
            printWriter.println(request);
            printWriter.flush();//刷新缓冲区
            //4、通过Scanner读取服务端响应并进行回显
            //读取服务端响应
            Scanner scanner=new Scanner(inputStream);
            String response=scanner.next();
            //响应内容回显到界面
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();//启动客户端
    }

}

单线程TCP客户端---服务端通信结果

单线程TCP存在的问题

当服务端启动之后,如果有客户端与服务端建立连接,服务端的accept方法就会返回一个socket对象,之后再通过processConnection方法对该客户端的socket不断进行while循环进行处理,调用scanner.next()方法,也就是说只要该客户端不下线,服务端就会一直在这个循环中,这将导致其他的想要建立连接的客户端无法完成。

当然,不使用while循环也是不可以的,因为如果没有while循环,客户端发送一次请求,服务端就会把连接给断掉,如果这个客户端还想继续建立连接,就需要重新建立连接,重新创建Socket对象,但是上述代码中的客户端只能创建一次socket,所以这时候就无法再建立连接了。

要想解决这个问题,就需要TCP服务端支持多线程进行操作。

TCP服务端(支持多个客户发送请求)

支持多个客户发送请求用到多线程的知识,可以提供多线程版本的服务端和线程池版本的服务端

多线程版本服务端

服务端的核心在于处理多个客户请求,也就是processConnection方法需要支持多线程,也就是说每个客户端的请求都会有一个线程进行处理。

多线程版本的服务端在客户端连接少的情况下是合适的,但是当客户端连接的量比较大的时候就不合适了,会有大量的线程的创建和销毁 ,资源耗费比较严重,所以可以考虑使用线程池来处理processConnection()方法。

线程池版本服务端

关于TCP的长连接和短连接

短连接工作过程

1、客户端与服务端建立连接

2、客户端向服务端发送请求

3、读取响应

4、关闭连接

特点:短连接一次通信一次连接,下一次通信需要重新建立连接,一次通信只会建立一次连接

长连接工作过程

1、客户端与服务端建立连接

2、客户端向服务端发送请求

3、读取响应

4、可根据需求再次发送请求(也是回到2)

5、重复2-4之间若干次,再决定是否关闭连接

特点:一次连接可多次发送请求,长连接的复用性更高

相关推荐
正在走向自律1 小时前
阿里云ESC服务器一次性全部迁移到另一个ESC
服务器·阿里云·云计算
gywl2 小时前
openEuler VM虚拟机操作(期末考试)
linux·服务器·网络·windows·http·centos
WTT00112 小时前
2024楚慧杯WP
大数据·运维·网络·安全·web安全·ctf
了一li3 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
杨德杰3 小时前
QT网络(一):主机信息查询
网络·qt
日记跟新中3 小时前
Ubuntu20.04 修改root密码
linux·运维·服务器
唐小旭3 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python
明 庭3 小时前
Ubuntu下通过Docker部署NGINX服务器
服务器·ubuntu·docker
BUG 4043 小时前
Linux——Shell
linux·运维·服务器