Java BIO TCP服务端向客户端消息群发代码教程实战

BIO

Java BIO(Blocking IO)是一种基于阻塞模式的IO模型,用于处理输入和输出操作。在使用Java BIO时,当一个线程调用输入或输出操作时,它会被阻塞,直到数据准备好或者操作完成。

BIO中最常使用的类是Socket类和ServerSocket类。Socket类表示一个客户端套接字,它可以与服务器进行连接和通信;ServerSocket类表示一个服务器套接字,它用于接受客户端的连接请求。使用这两个类,我们可以创建简单的网络应用程序。

下面是Java BIO使用的相关步骤:

  1. 创建ServerSocket对象:首先,我们需要创建一个ServerSocket对象来监听指定的端口,等待客户端的连接请求。

  2. 接受客户端连接:通过ServerSocket的accept()方法,我们可以接受客户端的连接请求,并返回一个代表客户端连接的Socket对象。

  3. 创建输入流和输出流:一旦连接建立,我们可以通过Socket对象的getInputStream()和getOutputStream()方法来获取与客户端通信的输入流和输出流。

  4. 数据传输:使用输入流从客户端读取数据,使用输出流向客户端发送数据。这些操作都是阻塞的,在数据准备好之前,线程将保持阻塞状态。

  5. 关闭连接资源:通信结束后,我们需要关闭输入流、输出流和Socket对象,以释放相关的资源。

尽管Java BIO的使用相对简单,但也存在一些限制和问题。最明显的问题是阻塞特性,当一个线程被阻塞时,它无法处理其他任务,从而导致性能下降。此外,BIO模型无法适应大规模并发连接的需求,每个连接需要一个独立的线程来处理,而线程资源是有限的。

在实际开发中,为了提高性能和并发能力,我们通常使用Java NIO(Non-blocking IO)或者Netty等框架,这些框架基于异步非阻塞的IO模型,可以更好地满足高并发和高性能的需求。

TCP

TCP 是一种面向连接的协议,通信双方在进行数据传输之前必须先建立连接。TCP 还支持窗口流量控制和拥塞控制机制,能够根据网络状况动态调整传输速率,提高网络吞吐量。下面是对 TCP 的详细解析。

1. 建立连接 TCP 使用三次握手来建立连接。首先,客户端发送一个有 SYN(同步)标志的包给服务器,进入 SYN-SENT 状态。然后,服务器回应一个 SYN/ACK 包作为响应,进入 SYN-RECEIVED 状态。最后,客户端再发送一个 ACK 包给服务器,确认连接建立。至此,连接建立成功,两个节点可以开始进行数据传输。

2. 数据传输 连接建立后,发送方会将数据分成多个报文段并依次发送给接收方。每个报文段都会带有序号(Sequence Number),用来保证接收端可以按正确的顺序重新组装数据。同时,发送方还会维护一个滑动窗口(Sliding Window),用来控制流量和确认收到的报文段。

3. 确认和重传 接收方会对收到的数据进行确认,以便发送方知道哪些数据已成功传送。确认是基于累计确认(Acknowledgment)原则的,即表示接收方准备好接收下一个序号之前的所有数据。如果发送方在一定时间内没有收到确认消息,它会重新发送相应的数据。这样可以保证数据的可靠性。

4. 拥塞控制 TCP 是一种可靠的传输协议,它能够适应不同网络状况下的变化,并通过控制拥塞窗口大小来调节传输速率。当网络负载过高时,发送方会根据检测到的丢包情况逐渐降低拥塞窗口大小,以减少网络拥塞。反之,如果网络负载较轻,则可以逐渐增加拥塞窗口大小,提高传输速率。

5. 关闭连接 当数据传输完成或者发生错误时,双方需要关闭连接。TCP 使用四次挥手来终止连接。首先,发送方发送一个有 FIN(结束)标志的包给接收方,进入 FIN-WAIT-1 状态。然后,接收方回应一个 ACK 包作为确认,进入 CLOSE-WAIT 状态。接下来,接收方发送一个有 FIN 和 ACK 标志的包给发送方,进入 LAST-ACK 状态。最后,发送方再发送一个 ACK 包作为确认,两个节点都进入 CLOSED 状态。

总之,TCP 是一种可靠性较高的协议,通过创新的连接管理、流量控制和拥塞控制机制,保证了数据的可靠传输。它在互联网中的广泛应用使得客户端和服务器能够稳定地通信,并实现各种网络服务。

前言 

   项目需要和第三方厂商的服务需要用TCP协议通讯,考虑到彼此双方可能都会有断网重连、宕机重启的情况,需要保证 发生上述情况后,服务之间能够自动实现重新通信。研究测试之后整理如下代码实现。因为发现客户端重启后,对于服务端来说原来的客户端和服务端进程进程已经关闭,启动又和服务端新开了一个进程。所以实现原理就可以通过服务端向客户端群发实现,断开重新连接通讯。

代码 

tcp服务端代码




import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class HttpSocketServer {
    public static void main(String[] args) {
        try {
            ServerSocket server=new ServerSocket(9020);
            while (true){
                Socket client=server.accept();
                client.setKeepAlive(true);
                client.setOOBInline(true);
                System.out.println("进入了1个客户机连接:"+client.getRemoteSocketAddress().toString());
                ServerThread st = new ServerThread(client);
                st.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

ServerThread 线程类

import java.io.*;
import java.net.Socket;

/**
 * 客户机   线程 ——自动执行run
 * @author Lenovo
 */
public class ServerThread extends Thread{

    private Socket client;

    /**
     * 方法描述: 用有参构造  接收主函数那边传来的 客户机
     */
    public ServerThread(Socket client) {
        this.client=client;
    }


    @Override
    public void run() {
        try {
            processSocket();//调用你想执行的 使线程启动时在run方法开始执行
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 调用以上方法
     */
    public void processSocket() throws IOException {
        //加入集合 便于服务器群发
        TcpTool.addSocket(client);
    }

TcpTool 消息群发工具类 


import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * 聊天工具类
 * @author tarzan
 */
public class TcpTool {

    private static List<Socket> clientList=new ArrayList<Socket>();

    /**
     * 便于 验证成功后  加入客户机
     * @param socket
     */
    public static void addSocket(Socket socket) {
        clientList.add(socket);
    }

    /**
     * 群发=遍历list中的all元素, 对每个元素 写出
     * @param msg
     * @throws IOException
     */
    public static void sendAll(String msg){
        for (int i = 0; i <clientList.size(); i++) {
            Socket client = clientList.get(i);
            if(clientIsClose(client)){
                delSocket(client);
                i--;
                continue;
            }
            try {
                OutputStream ops =  client.getOutputStream();
                ops.write((msg+"\r\n").getBytes());
                ops.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 判断是否断开连接,断开返回true,没有返回false
     * @param socket
     * @return
     */
    public static Boolean clientIsClose(Socket socket){
        try{
            //发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信
            socket.sendUrgentData(0xFF);
            // 发送一个数据包, 如果通信正常就不会报错.  没有报错说明没有关闭., 返回false
            return false;
        }catch(Exception se){
            return true;
        }
    }


    /**
     * 下线时删除
     * @param socket
     */
    public static void delSocket(Socket socket){
        clientList.remove(socket);
    }

}


Tcp客户端代码


import org.springblade.core.tool.utils.StringUtil;

import java.io.*;
import java.net.Socket;

/**
 * @author tarzan
 */
public class HttpSocketClient {
    public static void main(String[] args) throws IOException {
        Socket client=new Socket("127.0.0.1",9020);
        client.setKeepAlive(true);
        client.setOOBInline(true);
        while (true) {
            try {
                if (!clientIsClose(client)) {
                    InputStream is=client.getInputStream();
                    BufferedReader reader=new BufferedReader(new InputStreamReader(is));
                    String text=reader.readLine();
                    if(StringUtil.isNotBlank(text)){
                        System.out.println("来自服务端的消息:"+text);
                    }
                }else{
                    try {
                        //断开5秒后重新连接
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    client=new Socket("127.0.0.1",9020);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public static Boolean clientIsClose(Socket socket){
        try{
            //发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信
            socket.sendUrgentData(0xFF);
            // 发送一个数据包, 如果通信正常就不会报错.  没有报错说明没有关闭., 返回false
            return false;
        }catch(Exception se){
            return true;
        }
    }

运行一个服务端,启动多个客户端进行测试。

控制台输出

 以上只是实现的最简单的demo,服务端,因为服务端和客户端都需要不断监听彼此通信,发送消息时候,需要另起一个线程,调用TcpTool工具类想客户端群发消息。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泰山AI

原创不易,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值