Socket编程简单示例(聊天服务器)

 更新时间:2023年02月18日 14:15:14   作者:念念清晰  
socket编程是在不同的进程间进行网络通讯的一种协议,下面这篇文章主要给大家介绍了关于Socket编程简单示例的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

传统Socket基于BIO实现一个简单的聊天服务器

服务端代码如下

public class MyServerSocket {
  public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = new ServerSocket();
    // 绑定5000端口
    serverSocket.bind(new InetSocketAddress("127.0.0.1", 5000));
    System.out.println("服务端启动成功...");
    while (true) {
      // 如果获取不到socket就会一致阻塞在此
      Socket socket = serverSocket.accept();
      InputStream is = socket.getInputStream();
      byte[] buf = new byte[1024];
      int len = is.read(buf);
      System.out.println("客户端说:" + new String(buf, 0, len, StandardCharsets.UTF_8));
      OutputStream os = socket.getOutputStream();
      os.write("你好客户端,我收到你的消息了".getBytes(StandardCharsets.UTF_8));
    }
  }
}

客户端代码如下

public class ClientSocket {
  public static void main(String[] args) throws IOException {
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("127.0.0.1",5000));
    OutputStream os = socket.getOutputStream();
    os.write("hello服务端~".getBytes(StandardCharsets.UTF_8));
    InputStream is = socket.getInputStream();
    byte[] buf = new byte[1024];
    int len = is.read(buf);
    System.out.println("服务器说:" + new String(buf, 0, len, StandardCharsets.UTF_8));
  }
}

先启动服务器端,再启动客户端。即可

传统BIO是阻塞的,举个烧水的例子来理解

Socket编写一个简单的Http服务器

http服务器的代码

public class HttpServer {
  private static String response = """
      HTTP/1.1 200 OK
      content-type: text/html
            
      <h1>hello,client</h1>
      """;

  public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.bind(new InetSocketAddress("127.0.0.1", 5001));
    System.out.println("HTTP服务器启动成功");
    while (true) {
      Socket client = serverSocket.accept();
      // 获取客户端发送过来的数据
      InputStream is = client.getInputStream();
      byte[] buf = new byte[1024];
      int len = is.read(buf);
      System.out.println("客户端发送过来的数据:" + new String(buf, 0, len, StandardCharsets.UTF_8));
      // 给客户端响应HTTP协议的数据
      OutputStream os = client.getOutputStream();
      os.write(response.getBytes(StandardCharsets.UTF_8));
      // 注意:要关闭客户端资源
      client.close();
    }
  }
}

只要响应数据满足HTTP协议,就可以通过浏览器访问到页面,下面我们使用浏览器访问下

基于NIO的非阻塞简单服务器实现

传统BIO会阻塞,使用NIO通道编程可以设置服务器为非阻塞,当未获取到连接时,可以处理其他的逻辑。相当于线程模型换了。下面是服务端代码,客户端代码不变,采用BIO的即可

public class NioServerSocket {
  public static void main(String[] args) throws IOException, InterruptedException {
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 5002));
    serverSocketChannel.configureBlocking(false); // 设置非阻塞
    while (true) {
      SocketChannel clientChannel = serverSocketChannel.accept();
      if (clientChannel == null) {
        System.out.println("客户端无连接,休息一下");
        Thread.sleep(1000);
        continue;
      }
      Socket socket = clientChannel.socket();
      InputStream is = socket.getInputStream();
      byte[] buf = new byte[1024];
      int len = is.read(buf);
      System.out.println("客户端发送过来的数据:" + new String(buf, 0, len, StandardCharsets.UTF_8));
      socket.close();
      clientChannel.close();
    }
  }
}

第二种实现方式如下

public class NioServerSocket2 {
  public static void main(String[] args) throws IOException, InterruptedException {
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 5002));
    serverSocketChannel.configureBlocking(false); // 设置非阻塞
    while (true) {
      SocketChannel clientChannel = serverSocketChannel.accept();
      if (clientChannel == null) {
        System.out.println("客户端无连接,休息一下");
        Thread.sleep(1000);
        continue;
      }
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      int len = clientChannel.read(buffer);
      System.out.println("客户端说" + new String(buffer.array(), 0, len));
      clientChannel.close();
    }
  }
}

但是该实现也有一个问题:虽然客户端的连接过程不会阻塞了,但是客户端发送数据会阻塞服务端。如果客户端发送数据过大,假设要10秒,那服务端调用read方法读取数据就要等待客户端至少10秒。

基于NIO的Selector的简单服务器实现

selector的服务端如下,这是要给单线程的服务端。相比上一小节没有使用selector,它的优点就是连接事件和读事件都不会阻塞了。即使客户端发送数据很慢,服务端也不会阻塞。

缺点是单线程执行,如果一个线程抢到读就绪事件并且处理的很慢,就会影响整体性能。

public class NioSelectorServerSocket {
  public static void main(String[] args) throws Exception {
    // 1. 获取通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 5003));
    serverSocketChannel.configureBlocking(false);
    // 2. 获取选择器
    Selector selector = Selector.open();
    // 3. 把通道注册到选择器上,只注册连接继续事件
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    System.out.println("服务器启动成功...");
    while (true) {
      // 4. 不断轮询选择器中是否由连接事件
      int select = selector.select(2000);
      if (select == 0) {
        System.out.println("暂时没有客户端连接哦");
        continue;
      }
      // 5. 如果有连接继续事件,获取客户端通道
      Set<SelectionKey> selectionKeys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = selectionKeys.iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isAcceptable()) {
          SocketChannel client = serverSocketChannel.accept();
          //SocketChannel client = ((ServerSocketChannel) key.channel()).accept(); // 两种写法都一样
          client.configureBlocking(false);
          // 6. 为每个连接都注册写事件监听
          client.register(selector, SelectionKey.OP_READ);
          System.out.println("已注册可读事件");
        }
        if (key.isReadable()) {
          SocketChannel client = (SocketChannel) key.channel();
          // 7. 监听到可读事件,处理可读事件
          ByteBuffer buffer = ByteBuffer.allocate(1024);
          int len = client.read(buffer);
          System.out.println("客户端说:" + (len > 0 ? new String(buffer.array(), 0, len, StandardCharsets.UTF_8) : ""));
          // 8. 关闭资源
          client.close();
        }
        iterator.remove();
      }
    }
  }
}

它的线程模型还是用烧水的例子来举例

总结

到此这篇关于Socket编程简单示例的文章就介绍到这了,更多相关Socket编程示例内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java线程池的拒绝策略实现详解

    Java线程池的拒绝策略实现详解

    这篇文章主要介绍了Java线程池的拒绝策略实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Druid核心源码解析DruidDataSource

    Druid核心源码解析DruidDataSource

    这篇文章主要为大家介绍了Druid核心源码解析DruidDataSource,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • Java线程中的ThreadLocal类解读

    Java线程中的ThreadLocal类解读

    这篇文章主要介绍了Java线程中的ThreadLocal类解读,ThreadLocal是一个泛型类,作用是实现线程隔离,ThreadLocal类型的变量,在每个线程中都会对应一个具体对象,对象类型需要在声明ThreadLocal变量时指定,需要的朋友可以参考下
    2023-11-11
  • JDK生成WebService客户端代码以及调用方式

    JDK生成WebService客户端代码以及调用方式

    WebService 是一种跨编程语言和跨操作系统平台的远程调用技术,下面这篇文章主要给大家介绍了关于JDK生成WebService客户端代码以及调用方式的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-08-08
  • java创建线程的两种方法区别

    java创建线程的两种方法区别

    这篇文章主要为大家区分了java创建线程的两种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • IDEA中Java出现无效的源发行版错误的解决办法

    IDEA中Java出现无效的源发行版错误的解决办法

    这篇文章主要给大家介绍了关于IDEA中Java出现无效的源发行版错误的解决办法,IDEA中Java出现⽆效的源发⾏版解决办法出现该问题的原因是项⽬Project当中的jdk与电脑当中的jdk版本不⼀致造成的,需要的朋友可以参考下
    2023-10-10
  • Java使用junit框架进行代码测试过程详解

    Java使用junit框架进行代码测试过程详解

    单元测试就是针对最小的功能单元编写测试代码,Junit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用Junit编写单元测试。本文就来讲讲Junit框架的使用教程,需要的可以参考一下
    2023-02-02
  • 解决springboot的JPA在Mysql8新增记录失败的问题

    解决springboot的JPA在Mysql8新增记录失败的问题

    这篇文章主要介绍了解决springboot的JPA在Mysql8新增记录失败的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • SpringBoot无法访问webapp目录下的文件问题

    SpringBoot无法访问webapp目录下的文件问题

    这篇文章主要介绍了SpringBoot无法访问webapp目录下的文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • 超详细讲解Java异常

    超详细讲解Java异常

    Java 异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。本文超详细讲解了Java异常,感兴趣的小伙伴可以参考一下这篇文章
    2021-09-09

最新评论