【Javaee】网络编程-TCP Socket

news2024/10/21 5:39:24

前言

前文中我们介绍了UDP Socket相关的构造方法和方法,并实现了UDP的回显服务器和客户端。

本篇将介绍TCP Socket,并使用TCP Socket api实现服务器和客户端的通信


一.TCP Socket的常见方法

1.ServerSocket

ServerSocket是创建TCP服务端Socket的API

1)ServerSocket构造方法

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

2)ServerSocket方法

方法签名方法说明
Socket accept()开始监听指定端⼝(创建时绑定的端⼝),有客⼾端连接后,返回⼀个服务端Socket对象,并基于该 Socket建⽴与客⼾端的连接,否则阻塞等待
void close()

关闭此套接字

TCP建立连接的流程是操作系统内核完成的,代码感知不到,accept()操作是内核已经完成了连接建立的操作,accept()是确定对该连接进行处理(确认操作)。

如果没有客户端请求,则会阻塞。

accept会返回一个Socket对象,服务器每调用一次accept都会产生一个新的Socket对象,来和客户端进行“一对一服务”。

2.Socket

Socket 是客⼾端Socket,或是服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服务端Socket。

不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据的。

1)Socket构造方法

方法签名方法说明
Socket(String host,int Port)创建⼀个客⼾端流套接字Socket,并与对应IP的主机上,对应端⼝的进程建⽴连接

2)Socket方法

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

二.TCP服务器端实现

1.代码实现

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.security.Provider;
import java.util.Scanner;

public class TcpServer {
    private ServerSocket serverSocket=null;
    public TcpServer(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
           Socket client= serverSocket.accept();
           processConnection(client);
        }
    }

    private void processConnection(Socket client) {
        System.out.printf("[%s :%d]客户端上线",client.getInetAddress(),client.getPort());
        try(InputStream inputStream=client.getInputStream();//TCP是全双工通信,既能读,也能写
            OutputStream outputStream=client.getOutputStream();
            PrintWriter printWriter=new PrintWriter(outputStream)){
            while(true){
                //1.读取请求并解析(为方便读,可使用Scanner)
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    //如果Scanner读不出下一个数据了,说明客户端关闭了连接,导致服务器读到了“末尾”
                    break;
                }
                //2.根据请求计算响应
                String request=scanner.next();
                String response=process(request);
                //3.把响应写回客户端
                printWriter.println(response);
                //4.打印日志
                System.out.printf("[%s :%d], rep=%s,reps=%s\n",client.getInetAddress(),client.getPort(),
                        request,response);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        System.out.printf("[%s :%d]服务器下线",client.getInetAddress(),client.getPort());
    }
    private String process(String response){
        return response;
    }

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

2.代码解析

1.建立ServerSocket类,需填入参数来绑定程序的端口号,new ServerSocket(port)后于客户端建立起连接。

2.创建start()方法启动服务器,使用accept()确认对连接客户端进行处理,将处理过程传入processConnection()函数中

3.因TCP服务器是面向字节流,因此需要使用输入流与输出流进行数据的发送与接收。(在try中定义是因为try结束后会自动释放文件资源)

4.进行数据的读取,读取完后对数据进行处理(process()中处理,因本代码为回显服务器,只是返回客户端请求,无更多逻辑),处理完后生成响应,并写回客户端,最后打印日志

注:此处服务器代码存在三个问题,需结合客户端代码进行解释与修改

三.TCP客户端实现

1.代码实现

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 TcpCilent {
    private Socket socket=null;
    public TcpCilent(String ServerIP,int Port) throws IOException {
        socket=new Socket(ServerIP,Port);
    }
    public void strat(){
        System.out.println("客户端启动");
        try(InputStream inputStream= socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream();
            Scanner scanner=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            Scanner scannerIn=new Scanner(System.in)){
            while(true){
                //1.从控制台读取信息
                String request=scannerIn.next();
                //2.将数据发送给服务器
                printWriter.println(request);
                //3.从服务器获取响应
                if(!scannerIn.hasNext()){
                    break;
                }
                String response=scanner.next();
                //4.打印日志
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpCilent cilent=new TcpCilent("127.0.0.1",9090);
        cilent.strat();
    }
}

2.代码解析

1.客户端创建Socket类,需填入服务器的ip和端口号,在new时系统内核会自与对应的服务器连接上

2.与服务器同理,因TCP面向字节流,需要通过输入流与输出流来实现数据的发送与接收。

3.从控制台中读取数据(我们输入的),然后将其写入到服务器,在获取到服务器的响应,最后打印日志。

四.问题与解决

问题一:客户端发送数据之后,并没有任何响应(缓冲区问题)

运行上述代码,服务器成功打印出客户端上线和下线

但是客户端发送数据,服务器并无响应

原因是像PrintWriter这样的类,以及很多IO流中的类,都是自带“缓冲区”的。

引入缓冲区后,进行写入操作时不会立即触发IO,而是会将数据先放入缓冲区,等到缓冲区的数据积攒一波后,在统一发送(因为频繁的IO操作开销大,这是一个优化)

解决上诉问题,只要通过flush刷新缓冲区就可解决。

问题二:服务器代码未对client进行close()操作

这里的client是“连接级别”的数据,随着客户端断开连接,这个client也就不再使用了。

即使是同一个客户端再次连接,也是一个新的client,和旧的不是同一个

因此,这里的client就应该主动关闭掉。

可以在finally关闭

问题三:尝试多个客户端来同时连接服务器

作为一个服务器,就是要同时给多个客户端进行服务的

当前代码,只能启动一个客户端,如何启动多个客户端?

要修改一些配置(如图)

此时,我们发现,我们可以启动多个客户端

但是,利用新创建的客户端向服务器发送数据,却没有响应。

若把第一个客户端关掉,服务器就会立刻针对客户端2的请求进行响应。

此时,我们的服务器只能同时给一个客户端进行服务,这是不科学的。

原因是当第一个客户端连接上服务器,服务器代码就会进入processConnection内部的while循环,此时第二个客户端无法执行到accept,使用操作积压在内核缓冲区中。

解决方法是将双重while循环改为单层while循环,我们可以利用多线程

这样,问题就得到了解决

两个客户端是不同的端口

可以共同使用服务器。


完整代码

1.客户端代码

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 TcpCilent {
    private Socket socket=null;
    public TcpCilent(String ServerIP,int Port) throws IOException {
        socket=new Socket(ServerIP,Port);
    }
    public void strat(){
        System.out.println("客户端启动");
        try(InputStream inputStream= socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream();
            Scanner scanner=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            Scanner scannerIn=new Scanner(System.in)){
            while(true){
                //1.从控制台读取信息
                String request=scannerIn.next();
                //2.将数据发送给服务器
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器获取响应
                if(!scannerIn.hasNext()){
                    break;
                }
                String response=scanner.next();
                //4.打印日志
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpCilent cilent=new TcpCilent("127.0.0.1",9090);
        cilent.strat();
    }
}

2.服务器端代码

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.security.Provider;
import java.util.Scanner;

public class TcpServer {
    private ServerSocket serverSocket=null;
    public TcpServer(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
           Socket client= serverSocket.accept();
           Thread t=new Thread(()->{
               try {
                   processConnection(client);
               } catch (IOException e) {
                   e.printStackTrace();
               }
           });
            t.start();
        }
    }

    private void processConnection(Socket client) throws IOException {
        System.out.printf("[%s :%d]客户端上线\n",client.getInetAddress(),client.getPort());
        try(InputStream inputStream=client.getInputStream();//TCP是全双工通信,既能读,也能写
            OutputStream outputStream=client.getOutputStream();
            PrintWriter printWriter=new PrintWriter(outputStream)){
            while(true){
                //1.读取请求并解析(为方便读,可使用Scanner)
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    //如果Scanner读不出下一个数据了,说明客户端关闭了连接,导致服务器读到了“末尾”
                    break;
                }
                //2.根据请求计算响应
                String request=scanner.next();
                String response=process(request);
                //3.把响应写回客户端
                printWriter.println(response);
                printWriter.flush();
                //4.打印日志
                System.out.printf("[%s :%d], rep=%s,reps=%s\n",client.getInetAddress(),client.getPort(),
                        request,response);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            System.out.printf("[%s :%d]服务器下线\n",client.getInetAddress(),client.getPort());

            client.close();
        }

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

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

以上便是全部内容,如有不对,欢迎指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219831.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

线程池:高效管理并发任务的利器

线程池:高效管理并发任务的利器 什么是线程池? 线程池(Thread Pool)是Java并发编程中的一种设计模式,旨在通过重复利用线程资源,来提高程序执行效率。线程池的主要思想是提前创建一组可供使用的线程&#…

归一化输入

当输入的不同的特征取值范围差异过大,取得对应参数差别也会很大,在对参数进行优化的过程中,参数小的维度步长较小,参数大的维度步长较大,优化过程中路径曲折,将输入归一化,使特征取值范围差别小…

相控阵雷达电特性matlab模拟与仿真,带GUI界面,对比有限扫描阵,稀疏阵,多波束阵,共形阵等

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 相控阵雷达作为一种先进的雷达技术,具有高分辨率、多功能、快速扫描等优点,在军事和民用领域都有着广泛的应用。相控阵雷达的天线系统是其核心组成部分…

C#线性变换——缩放

前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在C#开发中经常需要对平面中的坐标进行一些变换,比如缩放、旋转等&…

数据结构:二叉树、堆

目录 一.树的概念 二、二叉树 1.二叉树的概念 2.特殊类型的二叉树 3.二叉树的性质 4.二叉树存储的结构 三、堆 1.堆的概念 2.堆的实现 Heap.h Heap.c 一.树的概念 注意,树的同一层中不能有关联,否侧就不是树了,就变成图了&#xff…

PCL 点云配准 Trimed-ICP算法(精配准

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 perform_standard_icp 函数 2.1.2 perform_trimmed_icp 函数 2.1.3 visualize_registration 函数 2.2完整代码 PCL点云算法汇总及实战案例汇总的目录地址链接: PCL点云算…

国庆旅游高峰期,如何利用可视化报表来展现景区、游客及消费数据

国庆黄金周,作为国内旅游市场的年度盛宴,总是吸引着无数游客的目光。今年,随着旅游市场的强劲复苏,各大景区又再次迎来游客流量的高峰。全国国内出游7.65亿人次,同比增长5.9%,国内游客出游总花费7008.17亿元…

大型企业软件开发是什么样子的? - Web Dev Cody

引用自大型企业软件开发是什么样子的? - Web Dev Cody_哔哩哔哩_bilibili 一般来说 学技术的时候 我们会关注 开发语言特性 ,各种高级语法糖,底层技术 但是很少有关注到企业里面的开发流程,本着以终为始(以就业为导向…

界面控件Telerik UI for WPF 2024 Q3亮点 - 支持禁用数据过滤等

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序,同时还能快速构建企业级办公WPF应用程序。UI for WPF支持MVVM、触摸等,创建的应用程序可靠且结构良好,非常容易维护,其直观的API将无缝地集成Visual Studio…

[Linux网络编程]03-TCP协议

一.TCP协议数据通信的过程 TCP数据报如下,数据报中的标志位双端通信的关键。 三次握手: 1.客户端向服务端发送SYN标志位,请求建立连接,同时发送空包 2.服务端向客户端回发ACK标志位(即确认标志位,任何一端发送数据后都需要另一端…

asyn驱动示例-int32driver

驱动程序源代码int32Driver.c&#xff1a; #include <stddef.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <ctype.h>#include <cantProceed.h> #include <epicsStdio.h> #i…

官龙村捐赠图书整理有感

今天&#xff08;2024年10月20日&#xff09;&#xff0c;我有幸参加了在深圳南山区西丽官龙村举行的义工活动&#xff0c;主要任务是整理捐赠的图书&#xff0c;并根据小学和中学的需求进行分类打包。这次活动不仅让我体会到了劳动的辛苦&#xff0c;更让我感受到了助人为乐的…

【AIGC】第一性原理下的ChatGPT提示词Prompt设计:系统信息与用户信息的深度融合

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;第一性原理与ChatGPT提示词Prompt设计应用第一性原理于ChatGPT提示词Prompt设计系统信息和用户信息的融合实际应用结论 &#x1f4af;系统信息与用户信息的定义和重要性系…

鸿蒙中富文本编辑与展示

富文本在鸿蒙系统如何展示和编辑的&#xff1f;在文章开头我们提出这个疑问&#xff0c;带着疑问来阅读这篇文章。 富文本用途可以展示图文混排的内容&#xff0c;在日常App 中非常常见&#xff0c;比如微博的发布与展示&#xff0c;朋友圈的发布与展示&#xff0c;都在使用富文…

C++高阶:红黑树实现

目录 一.红黑树的概念 1.1红黑树的规则 1.2红黑树的效率 二.红黑树的实现 2.1红黑树的结构 2.2红黑树的插入 2.2.1插入的大致过程 2.2.2情况一&#xff1a;变色 ​编辑 2.2.3情况二&#xff1a;单旋变色 2.2.4情况三&#xff1a;双旋变色 2.3插入代码实现 2.4红黑树的…

【网络安全】简单P1:通过开发者工具解锁专业版和企业版功能

未经许可,不得转载。 文章目录 前言发现过程前言 在探索一个SaaS平台的过程中,我发现了一个漏洞,使得我能够在无需订阅的情况下解锁高级(专业/企业)功能。 发现过程 我使用一个没有任何高级功能的基本用户账户进行常规登录。在浏览平台时,我注意到某些按钮和功能上带有…

【C++STL】list的基本介绍与使用方式

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f525; 所属专栏&#xff1a;C深入学习笔记 &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、list的介绍 文档内容以及大致翻…

ROS笔记之kill掉所有ros节点rosnode

ROS笔记之kill掉所有ros节点rosnode 文章目录 ROS笔记之kill掉所有ros节点rosnode1. 杀死所有 ROS 节点&#xff08;不包括 rosmaster&#xff09;2. 杀死 rosmaster3. 验证所有节点和 rosmaster 是否已终止4. roscore 和 rosmaster 是同一个概念吗&#xff1f;5. 为什么执行 k…

【踩坑日记36】ModuleNotFoundError: No module named ‘taming‘

问题描述 ModuleNotFoundError: No module named ‘taming‘问题分析 未正确安装taming-transformers包 问题解决 从github网站中安装taming-transformers包&#xff1a; 从github网站中下载taming-transformers代码 git clone https://github.com/CompVis/taming-transfo…

微机原理与接口技术知识点总结——绪论

1.1、计算机发展概述 早期计算机的用途主要是用于算数&#xff0c;一直到20世纪电子领域基础研究的突破&#xff0c;也就是电子计算机的诞生&#xff0c;计算机才变得如此广泛。以微电子技术为基础制造的电子计算机已经完全取代了机械计算机和机电计算机&#xff0c;因此电子计…