netty04(重点来了、指定某个客户端发信息或者群发)小声嘀咕~~我也是从零开始学得、、、想学习netty的又不知道怎么下手的童鞋们~~
还是和上几篇一样,先给出前面笔记的连接,有没看的可以去看看再来!
看到这里、你基本上可以使用netty接受信息和根据对应的信息返回信息了
接下来我们在web项目中使用,通过访问去启动,通过请求去给指定的客户端发信息,所以说到这篇就是重点了,我们不讲底层,不讲理论,直接上代码!
因为自己就是从零开始的,一上来你就给我讲一大堆理论、底层、我就mmp哦!了解底层我个人觉得你总是得会基本的使用了然后再开始去理解底层吧,不然那么枯燥无味的事情,谁会喜欢?
那我们就接着上一篇的来哈!
package com.netty.nettys01; import com.service.NettyTest01; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelId; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.util.concurrent.GlobalEventExecutor; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import static org.apache.http.Consts.UTF_8; public class DiscardServerHandler extends ChannelInboundHandlerAdapter { /** * 在这里使用DefaultChannelGroup将每次连接的 Channel 存起来,每一个Channel 底层就是一个连接 */ private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); private static final Map map = new HashMap<String,ChannelId>(); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { /** * 这里就是添加了 */ channels.add(ctx.channel()); /** * 获取每一个连接的唯一标示吧,相当于 */ ChannelId Id= ctx.channel().id(); /** * map就不用解释了吧,这里就用 1 代替了key;正常项目的话肯定使用用户的唯一标示了 */ map.put("1",Id); /** * 这里只是写了个测试遍历了一下 */ Iterator<Channel> ls= channels.iterator(); while (ls.hasNext()){ Channel l= ls.next(); System.out.println(l.id().toString()); } } /** * 下面这两个get方法是个人使用方法,不好的还希望大家给点意见 * @return */ public static ChannelGroup getChannels() { return channels; } public static Map getMap() { return map; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf in=(ByteBuf)msg; System.out.println(ctx.channel().id().toString()+"收到信息:"+in.toString(UTF_8)); String reMsg="66666"; ByteBuf in2= Unpooled.copiedBuffer(reMsg.getBytes()); ctx.writeAndFlush(in2); new NettyTest01().test(null,null); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
对于上面的代码,虽然有注释了。不过我还是说一下大概思路吧
channelActive 这个方法会在每次有连接进来的时候执行!
DefaultChannelGroup 这个对象可以用来存储连接,其实我觉得用其他的存也可以哈,不过既然netty本身提供了,我想应该有它自己的优势吧
最后使用一个map存下了登录的唯一标示;虽然key写死了为 1;也就是每次登录会覆盖上一个;这里就只模拟指定发给最后一个登录的客户端了,保存的方式有很多,基于每个项目架构的不同,我也就不多说了!
下面是发信息的类
package com.service; import com.netty.nettys01.DiscardServerHandler; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelId; import io.netty.channel.group.ChannelGroup; import org.springframework.stereotype.Service; import java.util.Iterator; import java.util.Map; @Service public class NettyTest01 { public void test(ChannelId id,String key){ /** * 每次调用获取所有的连接; */ ChannelGroup channelGroup=DiscardServerHandler.getChannels(); Map<String,ChannelId> map= DiscardServerHandler.getMap(); if (key!=null){ ChannelId id2= map.get("1"); Channel el= channelGroup.find(id2); el.writeAndFlush(Unpooled.copiedBuffer("99999".getBytes())); return; } Iterator<Channel> ls= channelGroup .iterator(); while (ls.hasNext()){ Channel l= ls.next(); System.out.println(l.id().toString()); l.writeAndFlush(Unpooled.copiedBuffer("1234545".getBytes())); } } }
接着说这个类的思路吧,这个类写在service层,交由spring管理了;自然就可以注入到你想注入的地方了!
调用方法判断是否传入key,如果不传入就群发,传入了就发给指定的,当然这里的规则就是你们自己定了
就是根据对应的key获取对应的连接,然后发送信息
下面的是controller 层 的访问启动服务代码,随意写个案例,我就随便放代码了(*╹▽╹*)
package com.controller; import com.netty.nettys01.DiscardServer; import com.service.NettyTest01; import com.service.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class Controller01 { @Autowired private TestService testService; @Autowired private NettyTest01 nettyTest01; @Autowired private ThreadPoolTaskExecutor threadPoolTaskExecutor; @RequestMapping("test2") public String test02() throws Exception { System.out.println(123); int c= threadPoolTaskExecutor.getActiveCount(); System.out.println("c="+c); nettyTest01.test(null,"1"); return "index"; } @RequestMapping("test4") @ResponseBody public String test04() throws Exception { new Thread(new Runnable() { public void run() { int port; /* if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; }*/ port = 8089; try { new DiscardServer(port).run(); }catch (Exception e){ System.out.println(123); } } }).start(); return "1"; } @RequestMapping("test3") @ResponseBody public int test03(){ System.out.println(123); /* for(int i=0;i<100;i++){ threadPoolTaskExecutor.execute(new Runnable() { public void run() { synchronized (Controller01.class) { try { String i= HttpUtils.get("http://192.168.31.223:8085/test4.do"); System.out.println(i); System.out.println(System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } }); }*/ return testService.get(); } }
说一下这个类吧,test4 请求是用来启动的,由于本身启动是堵塞的,所以使用线程启动。。。。。。
test2 是收到请求以后会给key为1的连接发送信息
回到上面的方法 可以看出 是给最后一个连接发送了信息 99999 这个就是指定的发了
如果传入两个 null ,就会群发了。。。这篇先说到这里 ,。。。因为我也还在学习。。。。。
可以在连接的时候将对应的id传于客户端,让客户端将id和用户id发回来保存,这样就可以根据用户id获取连接id了