首页 > 编程语言 >【Javaee】网络编程-TCP Socket

【Javaee】网络编程-TCP Socket

时间:2024-10-21 22:45:30浏览次数:9  
标签:java Socket Javaee TCP client 服务器 import 客户端

前言

前文中我们介绍了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();
    }
}

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

标签:java,Socket,Javaee,TCP,client,服务器,import,客户端
From: https://blog.csdn.net/yc_xym/article/details/143086674

相关文章

  • 九,网络编程UDP和TCP
    Java网络编程详解:从基础到实践网络编程是现代软件开发中不可或缺的一部分。在Java中,我们可以通过多种方式实现网络通信,其中最常用的是UDP和TCP协议。本文将详细介绍Java网络编程的基础知识、UDP和TCP编程的核心概念和实现方法。网络编程概述计算机网络定义计算机网络是指将地......
  • Javaee---多线程(一)
    文章目录1.线程的概念2.休眠里面的异常处理3.实现runnable接口4.匿名内部类子类创建线程5.匿名内部类接口创建线程6.基于lambda表达式进行线程创建7.关于Thread的其他的使用方法7.1线程的名字7.2设置为前台线程7.3判断线程是否存活8.创建线程方法总结9.start方法10.终......
  • 【Javaee】网络编程-UDP基础
     前言UDP是一个高效、快速、简单的传输协议,适合于需要低延迟和实时性的应用本篇将介绍UDP相关的api,并使用UDP构建回显服务器程序。一.UDP与TCP特点UDP:无连接,不可靠,面向数据报,全双工。TCP:有连接,可靠,面向字节流,全双工。何为连接?此处所说的连接是抽象的连接,并不是实际......
  • 【JS逆向百例】某赚网 WebSocket 套 Webpack 逆向分析
    声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作......
  • 如何使用WebSockets
    使用WebSockets你需要遵循以下步骤:一、理解WebSockets与传统HTTP的差异;二、选择合适的库和框架;三、建立WebSocket服务器;四、构建WebSocket客户端;五、确保连接的安全性。在开始使用WebSockets前,我们首先需要明白其背后的设计理念和技术特点。一、理解WebSockets与传统HTTP的差......
  • 流量抓包和网络问题排查,网工不要只会Wireshark,用好TCPdump才是大神!
    你好,这里是网络技术联盟站,我是瑞哥。在网络工程师的日常工作中,流量抓包和网络问题的排查是不可或缺的一环。Wireshark作为图形界面强大的流量分析工具,深受众多网络工程师的喜爱。然而,仅仅依赖Wireshark并不足以成为一个真正的网络排查高手。真正的网络大神往往能熟练运......
  • SpringBoot 使用WebSocket
    WebSocket是一种网络通信协议,提供了在单个TCP连接上进行全双工通信的能力。这意味着客户端和服务器可以同时发送和接收数据,而不需要等待对方的回应。SpringBoot提供了对WebSocket的自动配置和简化的编程模型,使得在SpringBoot应用程序中集成WebSocket变得相对简单。需要引入......
  • Qt编写的modbus模拟器/支持网络和串口以及websocket/支持网络rtu
    一、使用说明1.1设备模拟-Com第一步,填写要模拟的设备地址,0表示自动处理,也就是收到什么地址就应答什么地址。第二步,填写对应的串口号和波特率。第三步,单击打开串口,成功后会变成关闭串口字样。单击清空数据会将左侧打印栏的信息清空。右侧一堆微调框用于模拟对应设备多个寄......
  • TCP和UDP的报文格式
    TCP和UDP的报文格式  概要 了解TCP和UDP的报文格式对于网络通信、系统设计、故障排查和安全性等多个方面都非常重要。 一、TCP报文格式(TransmissionControlProtocol) TCP是面向连接、可靠的传输协议,其报文格式较复杂。TCP报文的格式如下:  上图简化如下:|......
  • java计算机毕业设计基于JavaEE的空竹传统文化交流平台(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着现代社会的快速发展,传统文化的传承面临着诸多挑战。在全球化和现代化的进程中,传统文化逐渐被边缘化,部分传统文化甚至面临失传的危险。空竹作......