JAVA Socket TCP 网络编程基础

TCP 网络编程

 

一、 JAVA-IO分类:

BIO,NIO,AIO (NIO 2.0)。

BIO 同步阻塞IO (blocking I/O): 服务器处理客户端的连接请求业务需要开启一个线程来进行业务,这样连接的资源越多服务器承载的消耗会变大。

NIO 同步非阻塞 (non-blocking I/O): 同步非阻塞,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时会得到需要处理的客户端对其进行处理。这种方式就大大减少了过多线程资源的开销。

AIO 异步非阻塞 (Asynchronous I/O) : 异步非阻塞,客户端连接或请求时,服务器的某些状态改变后通知对应处理方法。例如客户端连接服务器时,服务器知道客户端连接则通知处理accept方法,客户端发送给服务器数据,服务器知道有某客户端的数据则通知处理read方法。

 

NIO 实际上使用一个线程开始轮训选择有状态的客户端,发现某客户端有状态变化后挨个处理,这种处理方式业务实现上一般不要有过长的业务堵塞(例如 较长IO操作)。

AIO 实际上是有状态的客户端,调用一个线程来处理这部分业务。处理完之后如果还需要继续监听这个状态,则还需要重新注册。

 

以下代码可以copy到编辑器中调试,使用。

二、 BIO JAVA 实现

这里我实现一个简单的发送字符串的例子,服务器接受客户端发送的字符串,每一段完整的内容直接都使用\n进行分割。

客户端发送两条消息: 1234567890\n34567899\n

因为消息发送出来的时候,会出现两种情况:

    1. 两个数据包粘在一起需要拆包。

    2. 一个数据包第一次获取并不完整,分为多次获取才取完。

 

1. 服务器实现

BioServer.class

public class BioServer {

    public static void main(String[] args) throws Exception {
        // 创建一个服务器, 端口号 11111, 最大客户端连接: 10000
        ServerSocket server = new ServerSocket(11111, 10000);

        while (true) {
            // 等待接受客户端的连接,如果有客户端连接则会返回Socket对象,没有则阻塞。
            Socket client = server.accept();

            // 这里需要开启一个线程来保存与这个客户端的连接,然后继续接受下一个客户端。
			
            Thread thread = new Thread(new BioServerReaderHandler(client) ) ;
			
            thread.start(); // 启动线程
        }

    }
}

 

BioServerReaderHandler.class

public class BioServerReaderHandler implements Runnable {
    private Socket socket;

    public BioServerReaderHandler(Socket socket) {
        this.socket = socket;
    }
    
    @Override
    public void run() {
        try {
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();
            byte[] buff = new byte[1024];
            ByteArrayOutputStream cache = new ByteArrayOutputStream(1024);
            while (!socket.isClosed() && !socket.isInputShutdown()) {
                try {
                    int len = in.read(buff);
                    // 读到 \n 则输出
                    for (int i = 0; i < len; i++) {
                        byte b = buff[i];
                        
                        // 通过 \n 用来分割每个数据包, 能够完整拆除一个包则直接输出
                        
                        if (b == '\n') {
                            
                            byte[] lineBytes = cache.toByteArray();
                            String text = new String(lineBytes, Charset.forName("UTF-8"));

                            // 客户端关闭客户端
                            if (text.trim().equals("quit")) return; 
                            // 输出内容
                            System.out.println(text);
                            // 返回应答
                            out.write("已经收到一条消息\n".getBytes(Charset.forName("UTF-8")));
                            // 清空缓存
                            cache = new ByteArrayOutputStream(1024);
                        } else {
                            
                            // 不满足条件则放入缓存
                            cache.write(b);
                        }
                        
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (Exception e) {
    
            }
        }
    }

}

2. 客户端实现

这里写一个类似 telnet 的客户端, 通过控制台输入获取一行内容发送给服务器,服务器回传的信息显示到控制台上。

发送服务器的消息和接受服务器回复的消息都是 \n 分割。

如下就实现了一个简单的客户端,控制台的消息发送给服务器,服务器回复的消息再显示到控制台。

BioClient.class

public class BioClient {
    public static void main(String[] args) throws Exception {
        // 这里写一个类似 telnet 的客户端
        Socket client = new Socket("127.0.0.1", 11111);

        // 这里开启一个线程异步读取服务器返回的消息
        Thread thread = new Thread(new BioClientReaderHandler(client));
        thread.start();
        
        // 获得向服务器输出的流对象
        OutputStream out = client.getOutputStream();
        Scanner in = new Scanner(System.in);
        
        // 监听控制台写入的内容,发送给服务器
        while (!client.isClosed() && !client.isOutputShutdown()) {
            // 从控制台中读取一行数据  加上 \n 分割符 发送给服务器
            String line  = in.nextLine();
            
            byte[] lineBytes = (line  + "\n").getBytes(Charset.forName("UTF-8"));
            out.write(lineBytes);
            // 退出
            if (line.trim().equals("quit")) {
                break;
            }
        }
        
        client.close();
    }
}

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值