• Java-socket编程(建立连接)


    socket编程是以IO为理论基础的,理论学得差不多也很难实现编程,毕竟里面的类和方法平时都不怎么用,难得尝试编了个程,记录一下。

    1.几个概念

    Channel:管道,连通客户端和服务端传输数据;

    Buffer:缓冲区,通过管道传输数据必须经过的地方;

    Selector:选择器,单线程可以通过选择器处理多个管道;本文没有使用;

    2.案例功能

    启动服务端,再启动客户端与服务端连接,在客户端的控制台输入命令获取服务端的效果。本样例只处理了“time”命令,获取服务端的当前时间,其他命令都返回“非指定命令,无返回值”。详看代码注释。

    3.服务端代码

     1 import java.net.InetSocketAddress;
     2 import java.nio.ByteBuffer;
     3 import java.nio.channels.ServerSocketChannel;
     4 import java.nio.channels.SocketChannel;
     5 import java.nio.charset.StandardCharsets;
     6 import java.util.Date;
     7 import java.util.LinkedList;
     8 
     9 public class NIOService {
    10     static int PORT = 9011;
    11     public static void main(String[] args) throws Exception{
    12         //存储客户端连接
    13         LinkedList<SocketChannel> clients = new LinkedList<>();
    14         //1.服务端开启监听:接受客户端
    15         ServerSocketChannel ss = ServerSocketChannel.open();
    16         ss.bind(new InetSocketAddress(PORT));
    17         //2.只接受客户端,不阻塞
    18         ss.configureBlocking(false);
    19 
    20         while (true) {
    21             // 接受客户端的连接
    22             // client在Java层面是一个对象,在内核层面是一个fd
    23             SocketChannel client = ss.accept();
    24             if (client == null) {
    25                 //while循环进来没有 连到客户端就不管
    26             } else {
    27                 //和client传输数据使用的socket->fd
    28                 client.configureBlocking(false);
    29                 //获取客户端的端口号
    30                 int port = client.socket().getPort();
    31                 System.out.println("接收到客户端的连接,client port: " + port);
    32                 //将客户端添加到列表里
    33                 clients.add(client);
    34             }
    35             //可以在堆里,堆外,相关内容,可以看看JVM直接内存
    36             ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
    37 
    38             //遍历已经链接进来的客户端 的管道channel里有没有数据
    39             for (SocketChannel c : clients) {
    40                 //每循环一次都是一次系统调用,都是一次用户内核态的切换
    41                 int num = c.read(buffer);
    42                 if (num > 0) {
    43                     buffer.flip();
    44                     byte[] bytes = new byte[buffer.limit()];
    45                     buffer.get(bytes);
    46                     String s = new String(bytes);
    47                     System.out.println("端口为"+c.socket().getPort() + "的客户端发来命令:" +s);
    48                     String res = "";
    49                     ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4096);
    50                     if("time".equals(s)){
    51                         //获取时间
    52                         long l = System.currentTimeMillis();
    53                         res = "time: "+new Date(l).toString();
    54                         System.out.println(res);
    55                     }else{
    56                         res = "非指定命令,无返回值";
    57                     }
    58                     byteBuffer.put(res.getBytes(StandardCharsets.UTF_8));
    59                     byteBuffer.flip();
    60                     c.write(byteBuffer);
    61                 }
    62                 buffer.clear();
    63             }
    64         }
    65     }
    66 }

     4.客户端代码

     1 import java.io.*;
     2 import java.net.Socket;
     3 import java.nio.channels.ServerSocketChannel;
     4 import java.nio.channels.SocketChannel;
     5 import java.util.Scanner;
     6 
     7 public class Client {
     8     public static void main(String[] args) {
     9         try {
    10             //建立一个客户端连到9010的服务端
    11             Socket client = new Socket("127.0.0.1",9011);
    12             //设置发送命令的长度
    13             client.setSendBufferSize(20);
    14             /**
    15              * 关闭Nagle算法:该算法是将多个命令打包一起发送给服务端,避免网络拥挤。
    16              * 但是现在网络宽松,随便发也没事。所以关闭。
    17              */
    18             client.setTcpNoDelay(false);
    19 
    20             OutputStream out = client.getOutputStream();
    21             InputStream input = client.getInputStream();
    22             InputStream in = System.in;
    23             BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    24             Scanner scan = new Scanner(System.in);
    25             while(true){
    26                 String line = scan.nextLine();
    27                 //String line = reader.readLine();
    28                 if(line != null ){
    29                     //将输入的命令用字节数组存起来,通过客户端client的输出流发送给服务端
    30                     byte[] bb = line.getBytes();
    31                     out.write(bb);
    32                 }
    33                 byte b[] = new byte[1024];
    34                 /**
    35                  从输入流里读 东西 到 b数组,如果没有东西,就会一直读,阻塞。
    36                  len获取当前输入流里的内容长度,英文占1个长度,中文占3个长度
    37                  */
    38                 int len = input.read(b);
    39                 System.out.println(new String(b));
    40             }
    41         } catch (IOException e) {
    42             e.printStackTrace();
    43         }
    44     }
    45 
    46 }
  • 相关阅读:
    SQL SERVER 和EXCEL的数据导入导出
    常用SQL语句
    ASP.NET 中 ContentType 类型
    Centos7 安装MongoDB
    Scrapy:pipeline管道的open_spider、close_spider
    pipreqs(找当前项目依赖的包)
    Docker版本Jenkins的使用
    CentOS安装nginx,部署vue项目
    centos7中安装mysql
    flask框架使用flaskmigrate进行数据库的管理,非常方便!!!
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/14516451.html
Copyright © 2020-2023  润新知