socket编程

分为服务端和客户端,服务端采用NIO的模式,由于传统的模式可能存在消息堵塞的情况,可能需要用到多线程技术,实现起来难度较大,且耗cpu性能,没有特别要求的,或者对多线程技术不是很了解的,不建议使用,这里NIO可以很好解决这种问题JDK1.4以上就支持NIO了,我这里的程序都是在JDK1.8的环境下测试过,请大家参考,另外如果用到和前端页面的消息交互的话可以用websocket,例如网页聊天,请看我的另一篇博客https://blog.csdn.net/u011267841/article/details/103611624

1.服务端

package com.example.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@PropertySource("classpath:socket.properties")
public class NIOServer {

    @Value("${port}")
    private Integer port;
    
	  public void startNIO() throws IOException{

			//Integer port=8080;
			
		    // 创建网络服务端ServerSocketChannel
		    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		    // 设置为非阻塞模式
		    serverSocketChannel.configureBlocking(false);

		    // 构建一个Selector选择器,并且将channel注册上去
		    Selector selector = Selector.open();
		    // 将serverSocketChannel注册到selector
		    SelectionKey selectionKey = serverSocketChannel.register(selector, 0, serverSocketChannel);
		    // 对serverSocketChannel上面的accept事件感兴趣(serverSocketChannel只能支持accept操作)
		    selectionKey.interestOps(SelectionKey.OP_ACCEPT);

		    // 绑定端口
		    serverSocketChannel.socket().bind(new InetSocketAddress(port));
		    log.info("启动成功,占用端口号:"+port);

		    while (true) {
		      // 不再轮询通道,改用下面轮询事件的方式.select方法有阻塞效果,直到有事件通知才会有返回
		      selector.select();
		      // 获取事件
		      Set keys = selector.selectedKeys();
		      // 遍历查询结果
		      Iterator iterator = keys.iterator();
		      while (iterator.hasNext()) {
		        // 被封装的查询结果
		        SelectionKey key = iterator.next();
		        iterator.remove();
		        // 关注 Read 和 Accept两个事件
		        if (key.isAcceptable()) {
		          ServerSocketChannel server = (ServerSocketChannel) key.attachment();
		          // 将拿到的客户端连接通道,注册到selector上面
		          SocketChannel clientSocketChannel = server.accept();
		          clientSocketChannel.configureBlocking(false);
		          clientSocketChannel.register(selector, SelectionKey.OP_READ, clientSocketChannel);
		          log.info("收到新连接 : " + clientSocketChannel.getRemoteAddress());
		        }
		        if (key.isReadable()) {
		          SocketChannel socketChannel = (SocketChannel) key.attachment();
		          try {
		            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
		            while (socketChannel.isOpen() && socketChannel.read(byteBuffer) != -1) {
		              // 长连接情况下,需要手动判断数据有没有读取结束 (此处做一个简单的判断: 超过0字节就认为请求结束了)
		              if (byteBuffer.position() > 0) break;
		            }

		            if (byteBuffer.position() == 0) continue;
		            byteBuffer.flip();
		            byte[] content = new byte[byteBuffer.limit()];
		            byteBuffer.get(content);
		            log.info(new String(content));
		            log.info("收到数据,来自:" + socketChannel.getRemoteAddress());

		            // 响应结果 200
		            String response = "HTTP/1.1 200 OK\r\n" + "Content-Length: 11\r\n\r\n" + "Hello World";
		            ByteBuffer buffer = ByteBuffer.wrap(response.getBytes());
		            while (buffer.hasRemaining()) {
		              socketChannel.write(buffer);
		            }

		          } catch (Exception e) {
		            e.printStackTrace();
		            key.cancel(); // 取消事件订阅
		          }
		        }

		        selector.selectNow();
		      }
		    }
		  
	  }
	}

注意port就是你的配置文件的端口号,在resources目录下,我这里配置的是8080

2.客户端程序采用普通的socket的写法即可

package com.example.nio;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;
import java.util.UUID;

@Slf4j
public class Client {

    public static void main(String[] args) throws IOException {
        String host = "localhost";
        int port = 8080;

        //与服务端建立连接
        Socket socket = new Socket(host, port);
        socket.setOOBInline(true);
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String sendData = scanner.nextLine();
            log.info("输入内容为:"+sendData);
            socket.getOutputStream().write(sendData.getBytes("UTF-8"));
            if("end".equals(sendData)) {
                //结束会话
                break;
            }
        }

    }
}

3.SpringBoot启动程序


@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) throws BeansException, IOException {
        //SpringApplication.run(DemoApplication.class, args);
    	//起socket服务
        ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
        applicationContext.getBean(NIOServer.class).startNIO();//在spring容器启动后,取到已经初始化的NIOServer,启动Socket服务
        
        

        

    }

}

 

你可能感兴趣的:(网络编程,NIO,Socket编程,socket无阻塞,socket服务端,socket客户端)