java网络编程-第四节:tcp流套接字(serversocket)编程(代码片段)

快乐江湖 快乐江湖     2023-01-20     737

关键词:

文章目录

一:Java流套接字通信模型

Java TCP通信模型:Java中使用TCP协议进行通信,主要依靠以下两个类

  • ServerSocket:是创建TCP服务端Socket的API
  • Socket API:是客户端Socket,或服务端中接收到客户端连接(accept方法)的请求后,返回服务端Socket

通信流程如下

二:相关API详解

(1)ServerSocket

ServerSocket:用于创建TCP服务端流套接字Socket

构造方法如下

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

成员方法如下

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

(2)Socket

Socket :是客户端的Socket(当然也会给服务端用,上面表格说过,当有客户端连接服务端后,会返回一个服务端Socket

构造方法如下

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

成员方法如下

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

三:TCP通信示例一:客户端发送什么服务端就返回什么

  • 注意:这个功能比较简单,但主要目的是为了演示上面所讲API的用法

(1)代码

服务端IP地址设置为127.0.0.1,也即本地环回,也即自己发自己收,数据会完整走一遍协议

服务端TCPServer:

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 TCPServer 
    // 创建监听套接字
    private ServerSocket listenSocket = null;

    public TCPServer(int port) throws IOException 
        // 监听套接字绑定指定端口
        listenSocket = new ServerSocket(port);
    

    // 服务器启动
    public void start() throws IOException 
        System.out.println("服务器启动!");
        while (true) 
            // 调用监听套接字的accept()连接客户端,并返回Socket类型的clientSocket
            // 将clientSocket传递给具体处理连接的方法processConnection()进行处理
            Socket clientSocket = listenSocket.accept();
            // 进行处理
            processConnection(clientSocket);
        
    
    // 用于处理连接
    private void processConnection(Socket clientSocket) throws IOException 
        System.out.println("【客户端IP: " + clientSocket.getInetAddress().toString()
                + "客户端口号:" + clientSocket.getPort() + "】"
                + "已上线");

        // 处理请求
        // 打开inputStream和outputStream
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) 
            while (true) 
                // 1. 读取请求并解析
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()) 
                    // 如果读完了那么连接可以断开了
                    System.out.println("【客户端IP: " + clientSocket.getInetAddress().toString()
                            + "客户端口号:" + clientSocket.getPort() + "】"
                            + "下线");
                    break;
                
                String request = scanner.next();
                // 2. 根据请求计算响应,具体处理函数为process
                String response = process(request);

                // 3. 响应回复给客户端
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();

                // 打印信息
                System.out.println("【客户端IP: " + clientSocket.getInetAddress().toString()
                        + "客户端口号:" + clientSocket.getPort() + "】"
                        + ":\\"" + request + "\\"" + ", 服务端回复: " + "\\"" + response + "\\"");
            

         catch (IOException e) 
            e.printStackTrace();
         finally 
            // 关闭套接字
            // listenSocket在TCP服务端程序中只有一个,所以不太可能把文件描述符占满
            // 而clientSocket 每遇到一个客户端都要创建一个,所以一定要注意关闭
            clientSocket.close();
        
     

     // 业务逻辑函数
    public String process(String request) 
        return request;
    

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


客户端TPCClient:

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 TCPClient 
    // 建立Socket对象
    private Socket socket = null;

    public TCPClient(String serverIP, int serverPort) throws IOException 
        // 指定服务端IP和端口号
        socket = new Socket(serverIP, serverPort);
    

    // 客户端启动
    public void start () throws IOException 
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) 
            while (true) 
                // 1. 获取用户输入
                System.out.print("input: ");
                String request = scanner.next();
                // 2. 发送请求给服务端
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                printWriter.flush();
                // 3. 从服务端获得响应
                Scanner responseScanner = new Scanner(inputStream);
                String response = responseScanner.next();

                // 打印信息
                System.out.println("服务端回复:" + response);
            
         catch (IOException e) 
            e.printStackTrace();
        
    

    public static void main(String[] args) throws IOException 
        TCPClient client = new TCPClient("127.0.0.1", 9090);
        client.start();
    



(2)效果展示

(3)分析

对于服务端(TCPServer类)

  • 构造方法(public TCPServer(int port)

    • 需要建立一个ServerSocket类型的监听套接字,用于监听客户端的请求连接,也即private ServerSocket listenSocket = new ServerSocket(port)
  • 服务端处理逻辑(public void start()

    • 不断循环一直监听客户端的连接,当有客户端连接之后监听套接字会返回Socket类型的套接字用于处理这个连接,也即Socket clientSocket = listenSocket.accept()
    • 具体处理连接的过程交由processConnection()方法进行,也即processConnection(ClientSocket)
  • 服务端处理连接(private void processConnection(Socket clientSocket)

    • ①:打开套接字的输入流和输出流

      • clientSocket里的请求内容保存在其InputStream中,最终服务端回复响应时要将该响应写入到其OutputStream中,也即InputStream inputStream = clientSocket.getInputStream()OutputStream outputStream = clientSocket.getOutputStream()
    • ②:读取InputStream中的请求并解析

      • 使用Scanner进行读取比较方便,也即Scanner scanner = new Scanner(inputStream)
      • 读取时注意随时判断是否读取完毕,如果读取完毕表示客户端可以下线了
      • 读取好的请求保存在request中,也即String request = scanner.next()
    • ③:根据请求得到响应

      • request后,需要对该request进行处理(交给方法process),不同的业务逻辑会有不同的处理方法。这里我们只是简单的“回显”一下即可,也即客户端发什么服务端就回复什么
    • ④:将响应写入到OutputStream

      • 使用PrintWriter 写入比较方便,也即PrintWriter printWriter = new PrintWriter(outputStream)printWriter.println(response)
      • 写入完成之后必要忘记刷新一下,也即printWriter.flush()
    • ⑤:打印相关信息

    • ⑥:关闭clientSocket套接字

      • listenSocket在TCP服务端程序中只有一个,所以不太可能把文件描述符占满,而clientSocket 每遇到一个客户端都要创建一个,所以一定要注意关闭,也即 clientSocket.close()
  • main方法

    • 构造TCPServer对象,并绑定指定端口号,如9090,也即TCPServer server = new TCPServer(9090)
    • 启动服务端,也即server.start()

对于服务端(TCPClient类)

  • 构造方法(public TCPClient(String serverIP, int serverPort)

    • 需要建立一个Socket类型的套接字,并传入服务端IPPort,也即private Socket socket = new Socket(serverIP, serverPort)
  • 客户端处理逻辑(public void start ()

    • ①:打开套接字的输入流和输出流

      • 客户端会把它的请求写入到InputStream中,服务端回复响应后客户端会从 OutputStream 中读取,也即InputStream inputStream = socket.getInputStream()OutputStream outputStream = socket.getOutputStream()
    • ②:读取客户端用户输入并构造请求

      • 使用Scanner接受即可,也即String request = scanner.next()
    • ③:将请求写入到OutputStream

      • 使用PrintWriter 写入比较方便,也即PrintWriter printWriter = new PrintWriter(outputStream)printWriter.println(request)
      • 写入完成之后必要忘记刷新一下,也即printWriter.flush()
    • ④:读取InputStream中的响应

      • 使用Scanner进行读取比较方便,也即Scanner responseScanner = new Scanner(inputStream)String response = responseScanner.next()
    • ⑤:打印相关信息

  • main方法

    • 构造TCPClient对象,并给定服务端IPPort,也即TCPClient client = new TCPClient("127.0.0.1", 9090)
    • 启动客户端,也即client.start()

四:TCP通信示例二:多线程版本

(1)单线程版本存在的问题

上面的例子中,如果让多个客户端连接服务端会存在如下问题,以两个客户端为例

  • 客户端1连接服务端后,服务端提示“客户端1上线”
  • 客户端2连接服务端后,服务端未提示“客户端2上线”
  • 客户端1发送“客户端1”后服务端接受并正确返回
  • 客户端2发送“客户端2”后服务端似乎没有接受到消息,也没有什么反应
  • 客户端1结束运行,服务端提示“客户端1下线”,此时刚才客户端2发送的“客户端2”立刻显示同时服务端也正确回复

产生这样的现象原因在于服务端整个处理逻辑中只有一个线程,所以服务端在处理客户端1的请求时会被阻塞在下面代码中

 Socket clientSocket = listenSocket.accept();
 processConnection(clientSocket);


if (!scanner.hasNext()) 
    System.out.println("【客户端IP: " + clientSocket.getInetAddress().toString()
            + "客户端口号:" + clientSocket.getPort() + "】"
            + "下线");
    break;

当客户端1下线之后,服务端立马收到客户端2的请求然后才会去处理。所以要解决这个问题,整个代码逻辑必须使用多线程的方式进行改写

(2)代码

改写也比较简单,主线程持续监听客户端连接,每当一个客户端连接时便创建一个线程执行processConnection方法

public void start() throws IOException 
        System.out.println("服务器启动!");
        while (true) 
            // 调用监听套接字的accept()连接客户端,并返回Socket类型的clientSocket
            // 将clientSocket传递给具体处理连接的方法processConnection()进行处理
            // 主线程一直负责监听客户端连接
            Socket clientSocket = listenSocket.accept();
            // 每来一个客户端使用一个线程处理
            Thread thread = new Thread()
                @Override
                public void run()
                    try 
                        processConnection(clientSocket);
                     catch (IOException e) 
                        throw new RuntimeException(e);
                    
                
            ;
            thread.start();
        
    

(3)效果展示

如下,创建两个客户端

五:TCP通信示例三:线程池版本

(1)多线程版本存在的问题

采用多线程最大的问题在于当客户端数量一多就会涉及到频繁的线程创建和销毁,这开销会很大,所以为了减小创建销毁开销,同时也为了增加程序稳定性,这里我们可以使用线程池完成

(2)代码

改写也比较简单,主线程持续监听客户端连接,每当一个客户端连接时便使用线程池中的线程去执行processConnection方法

 public void start() throws IOException 
        System.out.println("服务器启动!");
        // 创建一个线程池
        ExecutorService service = Executors.newCachedThreadPool();
        while (true) 
            // 调用监听套接字的accept()连接客户端,并返回Socket类型的clientSocket
            // 将clientSocket传递给具体处理连接的方法processConnection()进行处理
            // 主线程一直负责监听客户端连接
            Socket clientSocket = listenSocket.accept();
            // 每来一个客户端使用线程池中的线程处理
            service.submit(new Runnable() 
                @Override
                public void run() 
                    try 
                        processConnection(clientSocket);
                     catch (IOException e) 
                        throw new RuntimeException(e);
                    
                
            );
        
    

(3)效果展示

推荐书单

...。有些名著,难以身临其境,就不能领悟其中精华。1.Java网络编程(第四版)java网络编程入门书籍,关于http、tcp协议、输入流、输出流、socket套接字、多线程、NIO,字节流等,介绍得很详细。2.Spring实战(第4版)Spring,作为J2EE... 查看详情

网络linuxlinux网络编程-tcp,udp套接字编程及代码示范(代码片段)

这里写目录标题UDP类UDP服务端单执行流UDP客户端TCP类TCP单执行流服务器TCP客户端TCP多执行流(线程)TCP多执行流(进程)本文我们分为TCP和UDP协议网络程序的编写:他们的区别如下:项目特点UDP用户数据报协议,无需连接&#... 查看详情

计算机网络详解--套接字编程(代码片段)

 目录1.什么是网络编程2.TCP/IP协议3.Socket套接字流套接字:使用传输层TCP(传输控制协议)数据报套接字:使用传输层UDP(用户数据报协议)原始套接字4.Java数据报套接字通信模型 UDP数据报套接字编程DatagramSocketAPIDa... 查看详情

javasocket底层是怎样基于tcp/ip实现的

...虚拟电路。在JAVA中,我们用ServerSocket、Socket类创建一个套接字连接,从套接字得到的结果是一个InputStream以及OutputStream对象,以便将连接作为一个IO流对象对待。通过IO流可以从流中读取数据或者写数据到流中,读写IO流会有异常... 查看详情

传输层-第四节:tcp流量控制(代码片段)

...王道考研408计算机网络+湖科大教书匠计算机网络+网络编程万字笔记、题目题型总结、注意事项、目录导航和思维导图王道考研408计算机组成原理万字笔记王道考研408数据结构+计算机算法设计与分析万字笔记王道考研408... 查看详情

cisco网络基础小实验第四节

第四章交换机划分VLAN配置本文讲述交换机VLAN问题,实验为同VLAN可通信,不同VLAN无法通信 查看详情

12.5-全栈java笔记:java网络编程

上节回顾:在学习了Socket在建立客户端和服务器单项通讯中,分别创建独立的Socket,并通过Socket的属性。那么如何将两个Socket进行连接,从而达到客户端和服务器之间建立输入输出流进行通信呢?在上节中我们已经讲到,TCP/IP套... 查看详情

通过 tcp 网络流读/写字符串

...时间】:2011-02-0214:24:35【问题描述】:我已经多次通过TCP套接字在应用程序之间发送二进制数据,但之前从未使用过字符串。碰到一个打算这样做的问题。这是我得到的:TcpClienttcpClient=newTcpClient("localhost",port);//ConnectsfineNetwor 查看详情

网络编程套接字之tcp(代码片段)

文章目录一、TCP流套接字编程ServerSocketSocketTCP长短连接二、TCP回显服务器客户端服务器客户端并发服务器UDP与TCP一、TCP流套接字编程我们来一起学习一下TCPsocketapi的使用,这个api与我们之前学习的IO流操作紧密相关,如果... 查看详情

java网络编程之tcp网络编程

一、基于Socket的TCP编程Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模型如图所示: 二、客户端Socket的工作过程(四个基本步骤)1、创建Socket根据指定服务端的IP地址或端口号构造Socket类对象。若服务器端响... 查看详情

传输层-第四节:tcp流量控制(代码片段)

获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航文章目录一:流量控制概述二:流量控制举例三:拓展阅读(可不看)(1)TCP流量控制完整例子(2)... 查看详情

《unix网络编程卷1:套接字联网api》学习笔记——tcp客户/服务器程序示例(代码片段)

UNIX网络编程——TCP客户/服务器程序示例概述TCP回射服务器程序:main函数TCP回射服务器程序:str_echo函数TCP回射客户程序:main函数TCP回射客户程序:str_cli函数正常启动正常终止服务器主机崩溃服务器主机崩溃后重... 查看详情

tcp和udp套接字编程(java实现)

在了解网络编程之前,我们先了解一下什么叫套接字套接字即指同一台主机内应用层和运输层之间的接口由于这个套接字是建立在网络上建立网络应用的可编程接口因此也将套接字称为应用程序和网络之间的应用程序编程接口!... 查看详情

网络编程套接字(tcp)(代码片段)

目录1、实现一个TCP网络程序(单进程版)        1.1、服务端serverTcp.cc文件                 服务端创建套接字                 服务端绑定                 服务端监听               ... 查看详情

原始套接字和普通的tcp套接字有啥不同?

...数据.1、使用原始套接字时应该注意的问题(参考<<unix网络编程>>以及网上的优秀文档)2、对于UDP/TCP产生的IP数据包,内核不将它传递给任何原始套接字,而只是将这些数据交给对应的UDP/TCP数据处理句柄(所以,如果你想要通过... 查看详情

java基础

...InetAddressaddress,intport)publicSocket(Stringhost,intport):创建客户端套接字对象,并且指定端口号和ip文本形式2)获取通道内的输出流对象3)给服务器端写数据4)释放资源j 查看详情

java网络编程(代码片段)

一、InetAddress类1.2常用方法实例化构造器全部隐藏,对外只提供静态方法进行对象的实例化InetAddressinet2=InetAddress.getByName("www.baidu.com");注意:IP的实例化和File的实例化一样,无论实际有没有这个IP,我都能... 查看详情

java网络编程:tcp的socket编程

一、Java中的网络编程协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构、交换方式、包含的意义以及怎样对报文所包含的信息进行解析,TCP/IP协议族有IP协议、TCP协议和UDP协议。现在TCP/IP协议族中... 查看详情