PHP 프로그래머 도 할 수 있 는 자바 개발 30 분 netty 로 고성능 웹 소켓 서 비 스 를 쉽게 만 들 수 있 습 니 다.

13976 단어 netty자바
경광파
SF 공식 홈 페이지 추천 'PHP 승급 의 길' (당신 은 또 얼마 동안 자신 에 게 투자 하지 않 았 습 니까? 먼저 보고 사 세 요)
우 리 는 다음 에 일부 실제 장면 을 모두 추가 합 니 다. 예 를 들 어 사용자 신분 의 검증, 관광객 들 은 발언 할 수 없고 여러 방 (채널) 의 채 팅 만 조회 할 수 있 습 니 다.이 블 로 그 는 자바 초보 자 에 게 매우 적합 하 며 자바 학습 의 착안점 으로 적합 하 며 tomcat, spring, my batis 등 을 고려 할 필요 가 없다.유일한 지식 포 인 트 는 마 벤 의 기초 사용 이다.
완전한 코드 주소
https://github.com/zhoumengka...
├── WebSocketServer.java                         
├── WebSocketServerInitializer.java          
├── WebSocketServerHandler.java           WebSocket    
├── dto
│   └── Response.java                             
├── entity
│   └── Client.java                          WebSocket        
└── service
    ├── MessageService.java                   
    └── RequestService.java             WebSocket             

기능 설계 개요
인증
클 라 이언 트 는 사용자 id, 들 어 온 방 의 rid, 사용자 token json_encode, 예 를 들 어 {id:1;rid:21;token:'43606811c7305ccc6abb2be116579bfd'}.그 다음 에 base64 에서 처리 하고 매개 변수 request 를 통 해 서버 에 전송 한 다음 에 서버 에서 id 와 token 의 검증 을 합 니 다. (제 방법 은 token 이 redis string 5 초 만 료 시간 에 저장 하 는 것 입 니 다)
룸 워 치
각 방 (채널) 을 저장 하기 위해 맵 channelGroupMap 을 사용 합 니 다. 클 라 이언 트 가 악 수 를 할 때 보 내 는 base 64 문자열 에서 정 의 된 방 ID 를 얻 은 다음 에 이 방 ID 를 새로 만 듭 니 다 ChannelGroup ChannelGroup.
pom. xml 에 netty 5 도입
현재 모두 자신의 가방 관리 도 구 를 가지 고 있 습 니 다. 다운 로드 를 실현 하지 않 고 로 컬 lib 라 이브 러 리 에 넣 습 니 다. nodejs 의 npm, phop 의 copser 와 같 습 니 다.

    
        io.netty
        netty-all
        5.0.0.Alpha2
    
    
        com.jcraft
        jzlib
        1.1.2
    
    
        org.json
        json
        20141113
    
    
        commons-codec
        commons-codec
        1.10
    

서버 생 성
이 코드 는 이해 해 야 합 니까?이것 은 netty 의 세트 입 니 다. 먼저 netty 의 스 레 드 모델 은 react 의 변형 이라는 것 을 기억 할 수 있 습 니 다. 여 기 는 두 개의 nio 스 레 드 그룹 이 있 습 니 다. 하 나 는 클 라 이언 트 의 요청 을 받 아들 이 는 것 이 고 하 나 는 worker 팀 이 클 라 이언 트 의 요청 을 전문 적 으로 처리 하 는 것 입 니 다.
아래 코드 를 간단하게 이해 할 수 있 도록 nginx 서버 를 구축 하 였 습 니 다.그 러 니까 신경 쓰 지 마.
package net.mengkang;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;


public final class WebSocketServer {

    private static final int PORT = 8083;

    public static void main(String[] args) throws Exception {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new WebSocketServerInitializer());

            Channel ch = b.bind(PORT).sync().channel();
            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
package net.mengkang;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;


public class WebSocketServerInitializer extends ChannelInitializer {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new HttpObjectAggregator(65536));
        pipeline.addLast(new WebSocketServerCompressionHandler());
        pipeline.addLast(new WebSocketServerHandler());
    }
}

처리 장 연결
다음 프로그램 에서 가장 좋 은 처 리 는 악수 단계 handleHttpRequest 에서 매개 변 수 를 처리 하 는 판단, 사용자 의 인증, 로그 인 사용자 표 의 유지, 생방송 방 표 유지 이다.상세 한 것 은 모두 코드 를 대조 하여 찾 아 보 세 요.악수 가 끝 난 후의 소식 전달 은 handleWebSocketFrame 에서 처리 된다.정 리 된 집행 절 차 는 여러분 이 각 방법의 중단 점 에 대해 디 버 깅 을 할 수 있 으 며 전체 집행 의 맥락 을 잘 알 수 있 을 것 입 니 다.
package net.mengkang;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.GlobalEventExecutor;
import net.mengkang.dto.Response;
import net.mengkang.entity.Client;
import net.mengkang.service.MessageService;
import net.mengkang.service.RequestService;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.netty.handler.codec.http.HttpHeaderNames.HOST;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class WebSocketServerHandler extends SimpleChannelInboundHandler {

    // websocket     uri
    private static final String WEBSOCKET_PATH = "/websocket";

    //    ChannelGroup         
    private static Map channelGroupMap = new ConcurrentHashMap <>();

    //       code
    private static final String HTTP_REQUEST_STRING = "request";

    private Client client = null;

    private WebSocketServerHandshaker handshaker;

    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        // Handle a bad request.
        if (!req.decoderResult().isSuccess()) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
            return;
        }

        // Allow only GET methods.
        if (req.method() != GET) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
            return;
        }

        if ("/favicon.ico".equals(req.uri()) || ("/".equals(req.uri()))) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
            return;
        }

        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.uri());
        Map> parameters = queryStringDecoder.parameters();

        if (parameters.size() == 0 || !parameters.containsKey(HTTP_REQUEST_STRING)) {
            System.err.printf(HTTP_REQUEST_STRING + "      ");
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
            return;
        }

        client = RequestService.clientRegister(parameters.get(HTTP_REQUEST_STRING).get(0));
        if (client.getRoomId() == 0) {
            System.err.printf("       ");
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
            return;
        }

        //                ,        ChannelGroup
        if (!channelGroupMap.containsKey(client.getRoomId())) {
            channelGroupMap.put(client.getRoomId(), new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
        }
        //       ,           
        channelGroupMap.get(client.getRoomId()).add(ctx.channel());

        // Handshake
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null, true);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            ChannelFuture channelFuture = handshaker.handshake(ctx.channel(), req);

            //       ,    
            if (channelFuture.isSuccess()) {
                if (client.getId() == 0) {
                    System.out.println(ctx.channel() + "   ");
                    return;
                }

            }
        }
    }

    private void broadcast(ChannelHandlerContext ctx, WebSocketFrame frame) {

        if (client.getId() == 0) {
            Response response = new Response(1001, "        ");
            String msg = new JSONObject(response).toString();
            ctx.channel().write(new TextWebSocketFrame(msg));
            return;
        }

        String request = ((TextWebSocketFrame) frame).text();
        System.out.println("    " + ctx.channel() + request);

        Response response = MessageService.sendMessage(client, request);
        String msg = new JSONObject(response).toString();
        if (channelGroupMap.containsKey(client.getRoomId())) {
            channelGroupMap.get(client.getRoomId()).writeAndFlush(new TextWebSocketFrame(msg));
        }

    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {

        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass().getName()));
        }

        broadcast(ctx, frame);
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
            HttpHeaderUtil.setContentLength(res, res.content().readableBytes());
        }

        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpHeaderUtil.isKeepAlive(req) || res.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println("  " + incoming.remoteAddress() + "     ");
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        if (client != null && channelGroupMap.containsKey(client.getRoomId())) {
            channelGroupMap.get(client.getRoomId()).remove(ctx.channel());
        }
    }

    private static String getWebSocketLocation(FullHttpRequest req) {
        String location = req.headers().get(HOST) + WEBSOCKET_PATH;
        return "ws://" + location;
    }
}

서버 쪽 에서 다 썼 습 니 다. 그리고 일부 클 라 이언 트 대상 의 구상 검증 같은 것 은 일일이 말 하지 않 습 니 다. 모두 간단 하고 코드 에 있 습 니 다.다음은 클 라 이언 트 입 니 다.
클 라 이언 트 프로그램



<script type="text/javascript">
var socket;
if (!window.WebSocket) {
  window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
  socket = new WebSocket("ws://localhost:8083/websocket/?request=e2lkOjE7cmlkOjI2O3Rva2VuOiI0MzYwNjgxMWM3MzA1Y2NjNmFiYjJiZTExNjU3OWJmZCJ9");
  socket.onmessage = function(event) {
      console.log(event.data);
  };
  socket.onopen = function(event) {
    console.log("websocket    ");
  };
  socket.onclose = function(event) {
    console.log("websocket    ");
  };
}

function send(message) {
  if (!window.WebSocket) { return; }
  if (socket.readyState == WebSocket.OPEN) {
    socket.send(message);
  } else {
    alert("The socket is not open.");
  }
}
</script>
<form onsubmit="return false;">
  <input type="text" name="message" value="Hello, World!"/>
  <input type="button" value="Send Web Socket Data" onclick="send(this.form.message.value)"/>
</form>

</code></pre> 
 <h2>    </h2> 
 <p>   https://github.com/ideal       <br/>https://github.com/zhoumengka...<br/>    N    ,       10   ,     2 4G  ,         ,    1500      ,       。<br/>     ,             ,       ,       。</p> 
 <p>      (               ),                  ,1500    ,   ,       0.5  。</p> 
 <h4>        ,     !</h4> 
 <ul> 
  <li>PHP      -    pv             </li> 
  <li>PHP      -    pv            </li> 
  <li>PHP      -            Java   </li> 
 </ul> 
</div>
                            </div>
                        </div>

좋은 웹페이지 즐겨찾기