【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接)

这篇具有很好参考价值的文章主要介绍了【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


网络编程

TCP流套接字编程

TCP提供的API主要是两个类:ServerSocket 和 Socket .
TCP不需要一个类来表示"TCP数据报"因为TCP不是以数据报为单位进行传输的.是以字节的方式,流式传输

ServerSocket API

ServerSocket 是专门给服务器使用的Socket对象.

ServerSocket 构造方法:

ServerSocket(int port) 创建一个服务端流套接字Socket,并绑定到指定端口.

ServerSocket 方法:

Socketaccept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于Socket建立与客户端的连接,否则阻塞等待.

【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式

voidclose() 关闭此套接字

Socket API

Socket是既会给客户端使用,也会给服务器使用.

Socket 构造方法:在服务器这边是有accept返回的.在客户端这边,在代码里构造的时候制定一个IP和端口号.(此处的IP和端口是服务器IP和端口)有了这个信息就能和服务器进行连接了.

Socket(String host, intport)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接.
【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式

Socket 方法:

进一步通过Socket对象获取内部的流对象,借助流对象来进行发送/接收.

InetAddress getInetAddress() 返回套接字所连接的地址
InputStream getInputStream() 返回此套接字的输入流
OutputStream getOutputStream() 返回此套接字的输出流

TCP中的长短连接

在TCP有连接的场景下,针对连接这个概念,有两种典型的表现形式.

  1. 短连接:客户端每次给服务器发消息,先建立连接,发送请求;下次再发送,则重新建立连接.

  2. 长连接:客户端建立连接后,连接先不断开,然后再次发送请求,读取响应;再发送请求,读取响应;若干轮之后,客户端确实短时间之内不再需要使用这个连接了,此时再断开.

两者区别:

  1. 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

拓展了解:
【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式

手写TCP版本的回显服务器

【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式

TCP服务端

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("启动服务器!");
        while (true) {
            //使用这个clientSocket和具体的客户端进行交流
            Socket clinentSocket = serverSocket.accept();
            processConnection(clinentSocket);
        }
    }
    //使用这个方法处理一个连接 一个连接对应一个客户端
    //可能涉及到多次交互
    private void processConnection(Socket clinentSocket) {
        //获得客户端IP和端口
        System.out.printf("[%s:%d] 客户端上线!\n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort());
        // 基于上述socket对象和客户端进行通信
        try(InputStream inputStream = clinentSocket.getInputStream();
            OutputStream outputStream = clinentSocket.getOutputStream()){
            //由于要处理多个请求和响应,也是使用循环
            while (true){
                //1. 读取请求
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()){
                    //没有下一个数据 说明读完了 (客户端关闭了连接)
                    System.out.printf("[%s:%d] 客户端下线!\n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort());
                    break;
                }
                //此处使用next是一直读取到换行符/空格/其他空白符结束
                //但是结果不包含上述空白符
                String request = scanner.next();
                //2. 根据请求响应
                String response = process(request);
                //3. 返回响应结果
                //outputStream没有write String这样的功能 可以把String里的字节数组拿出来 进行写入
                //也可以用字符流来转换一下
                PrintWriter printWriter = new PrintWriter(outputStream);
                //此处使用println来写入 让结果中带有一个\n 换行 方便对端来接收解析
                printWriter.println(response);
                //flush用来刷新缓冲区 保证当前写入的数据 确实是发送出去了
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s \n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort(),
                        request,response);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                clinentSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        return request;
    }
    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}

TCP客户端

public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
        //Socket 构造方法能够识别 电分十进制格式的IP地址 比DatagramPacket 更方便
        //new 这个对象的同时,就会进行 TCP 连接操作
        socket = new Socket(serverIp,serverPort);
    }

    public void start(){
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){
            while (true){
                //1. 先从键盘上读取用户输入的内容
                System.out.println("> ");
                String request = scanner.next();
                if(request.equals("exit")){
                    System.out.println("goodbye");
                    break;
                }
                //2.  把读到的内容构造成请求 发送给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                //加flush保证数据确实发送出去了
                printWriter.flush();
                //3. 读取服务器响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                //4. 把响应显示到界面上
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();
    }
}

问题:

当前代码里使用的是println来发送数据,println会在发送的数据后面自动带上\n换行.如果不适用println,而是使用print(不带\n换行) 上面的代码是否能正确运行?

不能正确运行,没有\n是不行的.TCP协议是面向字节流的协议(字节流特性:一次读多少个字节都行).接收方无法知道我们一次要多多少字节,这就需要我们在数据传输中进行明确的约定.此处代码中,隐式约定了使用\n来作为当前代码的请求/响应分割约定.
【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式
但是我们发现上面的代码还是存在一些问题的:
【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式
为解决上面的问题,我们使用多线程,主线程负责进行accept.每次接收到一个连接,创建新线程 ,由这个新的线程负责处理这个新的客户端.每个线程是独立的执行流.每个独立的执行流是各自执行各自的逻辑,彼此之间是并发关系.不会出现一边阻塞而影响到另一边执行的情况.

如果服务器,客户端特别多,很多客户端频繁建立连接就需要频繁创建/销毁线程了, 此时单纯的多线程的处理方法也不行了,所以我们就得用线程池来进行处理.

【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式

如果客户端非常多而且客户端连接都迟迟不断开,就会导致机器上有很多线程.如果一个服务器有几千个客户端就得是几千个线程.这个事情对机器来说,是一个很大的负担.这个时候为了解决单机支持更大量客户端的问题即C10M问题.就想办法让一个线程,处理多个客户端连接.为了解决这个问题操作系统底层提出了IO多路复用,IO多路转接.就是利用充分等待时间,做别的事情.我们给这个线程安排个集合,这个集合就放了一堆连接.这个线程就负责监听这个集合,哪个连接有数据来了,线程就来处理哪个连接,虽然连接有很多,总还是有先有后的.操作系统提供了一些原生API ,select,poll,epoll.在Java中,提供了一组NIO这样类,就封装了上述多路复用的API.

改进后:TCP服务器代码

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("启动服务器!");
        //此处使用CachedThreadPool ,使用FixedThreadPool不太合适(线程数不太应该是固定的 )
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while (true) {
            //使用这个clientSocket和具体的客户端进行交流
            Socket clinentSocket = serverSocket.accept();
            //此处使用多线程处理
            /*Thread t = new Thread(()->{
                processConnection(clinentSocket);
            });*/
            //使用线程池
            threadPool.submit(()->{
                processConnection(clinentSocket);
            });
        }
    }
    //使用这个方法处理一个连接 一个连接对应一个客户端
    //可能涉及到多次交互
    private void processConnection(Socket clinentSocket) {
        //获得客户端IP和端口
        System.out.printf("[%s:%d] 客户端上线!\n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort());
        // 基于上述socket对象和客户端进行通信
        try(InputStream inputStream = clinentSocket.getInputStream();
            OutputStream outputStream = clinentSocket.getOutputStream()){
            //由于要处理多个请求和响应,也是使用循环
            while (true){
                //1. 读取请求
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()){
                    //没有下一个数据 说明读完了 (客户端关闭了连接)
                    System.out.printf("[%s:%d] 客户端下线!\n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort());
                    break;
                }
                //此处使用next是一直读取到换行符/空格/其他空白符结束
                //但是结果不包含上述空白符
                String request = scanner.next();
                //2. 根据请求响应
                String response = process(request);
                //3. 返回响应结果
                //outputStream没有write String这样的功能 可以把String里的字节数组拿出来 进行写入
                //也可以用字符流来转换一下
                PrintWriter printWriter = new PrintWriter(outputStream);
                //此处使用println来写入 让结果中带有一个\n 换行 方便对端来接收解析
                printWriter.println(response);
                //flush用来刷新缓冲区 保证当前写入的数据 确实是发送出去了
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s \n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort(),
                        request,response);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                clinentSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        return request;
    }
    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}

【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接),网络,网络,tcp/ip,服务器,intellij-idea,java,网络协议,单例模式文章来源地址https://www.toymoban.com/news/detail-653782.html

到了这里,关于【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【网络编程】网络编程套接字(三)TCP网络程序

    与前边的UDP网络程序相同,创建套接字的接口都是socket,下边对socket接口进行介绍: 协议家族选择AF_INET,因为我们要进行网络通信。 而第二个参数,为服务类型,传入SOCK_STREAM,我们编写TCP程序,所以要选择流式的服务。 第三个参数默认传入0,由前两个参数就可以推出这是

    2024年02月16日
    浏览(33)
  • 【Linux网络】网络编程套接字(TCP)

    目录 地址转换函数 字符串IP转整数IP 整数IP转字符串IP 关于inet_ntoa 简单的单执行流TCP网络程序 TCP socket API 详解及封装TCP socket  服务端创建套接字  服务端绑定  服务端监听  服务端获取连接  服务端处理请求 客户端创建套接字 客户端连接服务器 客户端发起请求 服务器测试

    2024年03月21日
    浏览(44)
  • 【Linux】网络---->套接字编程(TCP)

    TCP的编程流程:大致可以分为五个过程,分别是准备过程、连接建立过程、获取新连接过程、消息收发过程和断开过程。 1.准备过程:服务端和客户端需要创建各自的套接字,除此之外服务端还需要绑定自己的地址信息和进行监听。注意:服务端调用listen函数后,处理监听状

    2024年02月04日
    浏览(39)
  • Linux网络编程——tcp套接字

    本章Gitee仓库:tcp套接字 客户端: 客户端: 关于构造和初始化,可以直接在构造的时候,将服务器初始化,那为什么还要写到 init 初始化函数里面呢? 构造尽量简单一点,不要做一些“有风险”的操作。 tcp 是面向连接的,通信之前要建立连接,服务器处于等待连接到来的

    2024年02月20日
    浏览(31)
  • 网络编程套接字之三【TCP】

    目录 1. ServerSocket API(给服务器端使用的类) 2. Socket API(既给服务器使用,也给客户端使用) 3. 写TCP回显—服务器 4. 使用线程池后的TCP服务器代码(最终) 5. 写回显-客户端 6. TCP回显—客户端代码 7. 运行回显服务器和客户端 TCP流套接字编程  ServerSocket 是创建TCP服务端Socket的

    2024年01月19日
    浏览(33)
  • 网络编程套接字(2)——简单的TCP网络程序

    我们将TCP服务器封装成一个类,当我们定义出一个服务器对象后需要马上对服务器进行初始化,而初始化TCP服务器要做的第一件事就是创建套接字。 TCP服务器在调用socket函数创建套接字时,参数设置如下: 协议家族选择 AF_INET ,因为我们要进行的是网络通信。 创建套接字时

    2024年02月06日
    浏览(35)
  • 【Linux网络编程】网络编程套接字(TCP服务器)

    作者:爱写代码的刚子 时间:2024.4.4 前言:本篇博客主要介绍TCP及其服务器编码 只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP地址 但是我们通常用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和in_addr表示之间转换 字符串转in

    2024年04月14日
    浏览(43)
  • TCP/IP网络编程(一) 理解网络编程和套接字

    网络编程和套接字概要 网络编程就是编写程序使两台联网的计算机相互交换数据 为了与远程计算机进行数据传输,需要连接因特网,而编程种的套接字就是用来连接该网络的工具。 构建套接字 1.调用soecket函数创建套接字 2.调用bind函数给套接字分配地址 3.调用listen函数将套

    2024年02月11日
    浏览(41)
  • 【网络通信】socket编程——TCP套接字

    TCP依旧使用代码来熟悉对应的套接字,很多接口都是在udp中使用过的 所以就不会单独把他们拿出来作为标题了,只会把第一次出现的接口作为标题 通过TCP的套接字 ,来把数据交付给对方的应用层,完成双方进程的通信 在 tcpServer.hpp 中,创建一个命名空间 yzq 用于封装 在命名

    2024年02月13日
    浏览(29)
  • JavaEE-网络编程套接字(UDP/TCP)

    下面写一个简单的UDP客户端服务器流程 思路: 对于服务器端:读取请求,并解析– 根据解析出的请求,做出响应(这里是一个回显,)–把响应写回客户端 对于客户端:从控制台读取用户输入的内容–从控制台读取用户输入的内容–从控制台读取用户输入的内容–将其显示在

    2024年02月07日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包