谁有那年花开月正圆微博最新百度云资源啊??

netty与websocket通信demo
netty v3.9.4
websocket建立前,客户端需要与服务器进行握手 确认websocket连接,也就是说在处理websocket请求前,必需要处理一些http请求。
websocket到现在为止,已经有多个版本,netty有相应的对应类,这部分处理一般不需要人工干预。
如果运行正常的话,会在页面的文本框中显示1-20记数。
可以通过firefox或chrome的开发人员工具,显看浏览器与服务器的交互。
主要是HttpServerChannelHandler2,加了些注释和少量debug代码。
package org.sl.demo.httpserver1;
import java.util.L
import java.util.M
import org.jboss.netty.channel.C
import org.jboss.netty.channel.ChannelHandlerC
import org.jboss.netty.channel.MessageE
import org.jboss.netty.channel.SimpleChannelH
import org.jboss.netty.handler.codec.http.DefaultHttpR
import org.jboss.netty.handler.codec.http.HttpH
import org.jboss.netty.handler.codec.http.HttpM
import org.jboss.netty.handler.codec.http.HttpR
import org.jboss.netty.handler.codec.http.HttpResponseS
import org.jboss.netty.handler.codec.http.HttpV
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketF
import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketF
import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketF
import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketF
import org.jboss.netty.handler.codec.http.websocketx.WebSocketF
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerH
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerF
public class HttpServerChannelHandler2 extends SimpleChannelHandler{
public static boolean debug =
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Channel ch = e.getChannel();
Object msg = e.getMessage();
if(debug){
System.out.println(&---------------&);
System.out.println(&message: &+msg.getClass());
//虽然是websocket,但在建立websocket连接前,先进行http握手,所以,这时也要处理http请求
//在http握手完成后,才是websocket下的通信
if(msg instanceof HttpRequest){
processHttpRequest(ch, (HttpRequest)msg);
}else if(msg instanceof WebSocketFrame){
processWebsocketRequest(ch,(WebSocketFrame)msg);
//未处理的请求类型
//这个方法:
//1.完成websocket前的http握手
//2.屏蔽掉非websocket握手请求
void processHttpRequest(Channel channel,HttpRequest request){
HttpHeaders headers = request.headers();
if(debug){
List&Map.Entry&String,String&& ls = headers.entries();
for(Map.Entry&String,String& i: ls){
System.out.println(&header
&+i.getKey()+&:&+i.getValue());
//屏蔽掉非websocket握手请求
//只接受http GET和headers['Upgrade']为'websocket'的http请求
if(!HttpMethod.GET.equals(request.getMethod())
|| !&websocket&.equalsIgnoreCase(headers.get(&Upgrade&))){
DefaultHttpResponse resp = new DefaultHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.BAD_REQUEST);
channel.write(resp);
channel.close();
WebSocketServerHandshakerFactory wsShakerFactory = new WebSocketServerHandshakerFactory(
&ws://&+request.headers().get(HttpHeaders.Names.HOST),
null,false );
WebSocketServerHandshaker wsShakerHandler = wsShakerFactory.newHandshaker(request);
if(null==wsShakerHandler){
//无法处理的websocket版本
wsShakerFactory.sendUnsupportedWebSocketVersionResponse(channel);
//向客户端发送websocket握手,完成握手
//客户端收到的状态是101 sitching protocol
wsShakerHandler.handshake(channel, request);
//websocket通信
void processWebsocketRequest(Channel channel, WebSocketFrame request){
if(request instanceof CloseWebSocketFrame){
channel.close();
}else if(request instanceof PingWebSocketFrame){
channel.write(new PongWebSocketFrame(request.getBinaryData()));
}else if(request instanceof TextWebSocketFrame){
//这个地方 可以根据需求,加上一些业务逻辑
TextWebSocketFrame txtReq = (TextWebSocketFrame)
if(debug){ System.out.println(&txtReq:&+txtReq.getText());}
//向ws客户端发送多个响应
for(int i=1; i&=20; i++){
channel.write(new TextWebSocketFrame(&&+i));
try{Thread.sleep(300);}catch(Exception ex){}
//WebSocketFrame还有一些
其他类跟网上的差不多:&http://blog.csdn.net/shagoo/article/details/8028813
package org.sl.demo.httpserver1;
import java.net.InetSocketA
import java.util.concurrent.E
import org.jboss.netty.bootstrap.ServerB
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelF
public class HttpServer implements Runnable{
int port = 80;
public HttpServer(int port){
this.port =
public void run() {
ServerBootstrap b = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
b.setPipelineFactory(new HttpServerChannelPipelineFactory());
b.setOption(&child.tcpNoDelay&, true);
b.setOption(&child.keepAlive&, true);
b.bind(new InetSocketAddress(port));
public static void main(String[] args){
new HttpServer(80).run();
package org.sl.demo.httpserver1;
import org.jboss.netty.channel.ChannelP
import org.jboss.netty.channel.ChannelPipelineF
import org.jboss.netty.channel.C
import org.jboss.netty.handler.codec.http.HttpRequestD
import org.jboss.netty.handler.codec.http.HttpResponseE
import org.jboss.netty.handler.codec.http.websocketx.WebSocket00FrameD
import org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameD
public class HttpServerChannelPipelineFactory
implements ChannelPipelineFactory {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline cp = Channels.pipeline();
cp.addLast(&decoder&, new HttpRequestDecoder());
cp.addLast(&decoder&, new WebSocket00FrameDecoder());
cp.addLast(&encoder&, new HttpResponseEncoder());
cp.addLast(&downhandler&, new HttpServerDownstreamHandler());
cp.addLast(&uphandler&, new HttpServerUpstreamHandler());
cp.addLast(&handler&, new HttpServerChannelHandler2());
测试页面:
function connect1(){
alert('connect1');
var ta = document.getElementById('responseText');
var socket = new WebSocket('ws://127.0.0.1/websocket');
if (window.WebSocket) {
alert('Your browser does not support Web Socket.');
socket.onopen = function(event) {
ta.value = &Web Socket opened!&;
socket.onmessage = function(event) {
ta.value = event.
socket.onclose = function(event) {
ta.value = &Web Socket closed&;
function connect() {
alert('connect');
if (!window.WebSocket) {
window.WebSocket = window.MozWebS
if (window.WebSocket) {
socket = new WebSocket(&ws://127.0.0.1/websocket&);
socket.onmessage = function(event) {
var ta = document.getElementById('responseText');
ta.value = event.
socket.onopen = function(event) {
var ta = document.getElementById('responseText');
ta.value = &Web Socket opened!&;
socket.send('hello');
socket.onclose = function(event) {
var ta = document.getElementById('responseText');
ta.value = &Web Socket closed&;
socket.onerror = function(event){
alert(&Your browser does not support Web Socket.&);
&input type=&button& onclick=&connect1()& value=&ws connect1&/& &br/&&br/&
&input type=&button& onclick=&connect()& value=&ws connect&/& &br/&&br/&
&input id=&responseText& type=&text& value=&123456&/&
您可能也会对以下文章感兴趣
QQ : 341470
Friend Link
New MemberNetty 实现 WebSocket 聊天功能 - ImportNew
原文同步至
上一次我们用
快速实现了一个 Java 聊天程序(见)。现在,我们要坐下修改,加入 WebSocket 的支持,使它可以在浏览器里进行文本聊天。
Maven 3.2.x
Eclipse 4.x
&!– more –&
WebSocket 通过“(升级握手)”从标准的 HTTP 或HTTPS 协议转为 WebSocket。因此,使用 WebSocket 的应用程序将始终以 HTTP/S 开始,然后进行升级。在什么时候发生这种情况取决于具体的应用;它可以是在启动时,或当一个特定的 URL 被请求时。
在我们的应用中,当 URL 请求以“/ws”结束时,我们才升级协议为WebSocket。否则,服务器将使用基本的 HTTP/S。一旦升级连接将使用的WebSocket 传输所有数据。
整个服务器逻辑如下:
1.客户端/用户连接到服务器并加入聊天
2.HTTP 请求页面或 WebSocket 升级握手
3.服务器处理所有客户端/用户
4.响应 URI “/”的请求,转到默认 html 页面
5.如果访问的是 URI“/ws” ,处理 WebSocket 升级握手
6.升级握手完成后 ,通过 WebSocket 发送聊天消息
让我们从处理 HTTP 请求的实现开始。
处理 HTTP 请求
HttpRequestHandler.java
public class HttpRequestHandler extends SimpleChannelInboundHandler&FullHttpRequest& { //1
private final String wsU
private static final File INDEX;
URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation();
String path = location.toURI() + &WebsocketChatClient.html&;
path = !path.contains(&file:&) ? path : path.substring(5);
INDEX = new File(path);
} catch (URISyntaxException e) {
throw new IllegalStateException(&Unable to locate WebsocketChatClient.html&, e);
public HttpRequestHandler(String wsUri) {
this.wsUri = wsU
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (wsUri.equalsIgnoreCase(request.getUri())) {
ctx.fireChannelRead(request.retain());
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx);
RandomAccessFile file = new RandomAccessFile(INDEX, &r&);//4
HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK);
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, &text/ charset=UTF-8&);
boolean keepAlive = HttpHeaders.isKeepAlive(request);
if (keepAlive) {
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length());
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
ctx.write(response);
if (ctx.pipeline().get(SslHandler.class) == null) {
ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
ctx.write(new ChunkedNioFile(file.getChannel()));
ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
file.close();
private static void send100Continue(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
ctx.writeAndFlush(response);
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
Channel incoming = ctx.channel();
System.out.println(&Client:&+incoming.remoteAddress()+&异常&);
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
1.扩展 SimpleChannelInboundHandler 用于处理 FullHttpRequest信息
2.如果请求是 WebSocket 升级,递增引用计数器(保留)并且将它传递给在 ChannelPipeline 中的下个 ChannelInboundHandler
3.处理符合 HTTP 1.1的 &# Continue” 请求
4.读取默认的 WebsocketChatClient.html 页面
5.判断 keepalive 是否在请求头里面
6.写 HttpResponse 到客户端
7.写 index.html 到客户端,判断 SslHandler 是否在 ChannelPipeline 来决定是使用 DefaultFileRegion 还是 ChunkedNioFile
8.写并刷新 LastHttpContent 到客户端,标记响应完成
9.如果 keepalive 没有要求,当写完成时,关闭 Channel
HttpRequestHandler 做了下面几件事,
如果该 HTTP 请求被发送到URI “/ws”,调用 FullHttpRequest 上的 retain(),并通过调用 fireChannelRead(msg) 转发到下一个 ChannelInboundHandler。retain() 是必要的,因为 channelRead() 完成后,它会调用 FullHttpRequest 上的 release() 来释放其资源。 (请参考我们先前的 SimpleChannelInboundHandler 在第6章中讨论)
如果客户端发送的 HTTP 1.1 头是“Expect: 100-continue” ,将发送“100 Continue”的响应。
在 头被设置后,写一个 HttpResponse 返回给客户端。注意,这是不是 FullHttpResponse,唯一的反应的第一部分。此外,我们不使用 writeAndFlush() 在这里 – 这个是在最后完成。
如果没有加密也不压缩,要达到最大的效率可以是通过存储 index.html 的内容在一个 DefaultFileRegion 实现。这将利用零拷贝来执行传输。出于这个原因,我们检查,看看是否有一个 SslHandler 在 ChannelPipeline 中。另外,我们使用 ChunkedNioFile。
写 LastHttpContent 来标记响应的结束,并终止它
如果不要求 keepalive ,添加 ChannelFutureListener 到 ChannelFuture 对象的最后写入,并关闭连接。注意,这里我们调用 writeAndFlush() 来刷新所有以前写的信息。
处理 WebSocket frame
WebSockets 在“帧”里面来发送数据,其中每一个都代表了一个消息的一部分。一个完整的消息可以利用了多个帧。 WebSocket “Request for Comments” (RFC) 定义了六中不同的 Netty 给他们每个都提供了一个 POJO 实现 ,而我们的程序只需要使用下面4个帧类型:
CloseWebSocketFrame
PingWebSocketFrame
PongWebSocketFrame
TextWebSocketFrame
在这里我们只需要显示处理 TextWebSocketFrame,其他的会由 WebSocketServerProtocolHandler 自动处理。
下面代码展示了 ChannelInboundHandler 处理 TextWebSocketFrame,同时也将跟踪在 ChannelGroup 中所有活动的 WebSocket 连接
TextWebSocketFrameHandler.java
public class TextWebSocketFrameHandler extends
SimpleChannelInboundHandler&TextWebSocketFrame& {
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
protected void channelRead0(ChannelHandlerContext ctx,
TextWebSocketFrame msg) throws Exception { // (1)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
if (channel != incoming){
channel.writeAndFlush(new TextWebSocketFrame(&[& + incoming.remoteAddress() + &]& + msg.text()));
channel.writeAndFlush(new TextWebSocketFrame(&[you]& + msg.text() ));
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush(new TextWebSocketFrame(&[SERVER] - & + incoming.remoteAddress() + & 加入&));
channels.add(ctx.channel());
System.out.println(&Client:&+incoming.remoteAddress() +&加入&);
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush(new TextWebSocketFrame(&[SERVER] - & + incoming.remoteAddress() + & 离开&));
System.out.println(&Client:&+incoming.remoteAddress() +&离开&);
channels.remove(ctx.channel());
public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
Channel incoming = ctx.channel();
System.out.println(&Client:&+incoming.remoteAddress()+&在线&);
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
Channel incoming = ctx.channel();
System.out.println(&Client:&+incoming.remoteAddress()+&掉线&);
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
Channel incoming = ctx.channel();
System.out.println(&Client:&+incoming.remoteAddress()+&异常&);
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
1.TextWebSocketFrameHandler 继承自 ,这个类实现了 接口,ChannelInboundHandler 提供了许多事件处理的接口方法,然后你可以覆盖这些方法。现在仅仅只需要继承 SimpleChannelInboundHandler 类而不是你自己去实现接口方法。
2.覆盖了 handlerAdded() 事件处理方法。每当从服务端收到新的客户端连接时,客户端的 Channel 存入 列表中,并通知列表中的其他客户端 Channel
3.覆盖了 handlerRemoved() 事件处理方法。每当从服务端收到客户端断开时,客户端的 Channel 移除 ChannelGroup 列表中,并通知列表中的其他客户端 Channel
4.覆盖了 channelRead0() 事件处理方法。每当从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel。其中如果你使用的是 Netty 5.x 版本时,需要把 channelRead0() 重命名为messageReceived()
5.覆盖了 channelActive() 事件处理方法。服务端监听到客户端活动
6.覆盖了 channelInactive() 事件处理方法。服务端监听到客户端不活动
7.exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。在大部分情况下,捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之前发送一个错误码的响应消息。
上面显示了 TextWebSocketFrameHandler 仅作了几件事:
当WebSocket 与新客户端已成功握手完成,通过写入信息到 ChannelGroup 中的 Channel 来通知所有连接的客户端,然后添加新 Channel 到 ChannelGroup
如果接收到 TextWebSocketFrame,调用 retain() ,并将其写、刷新到 ChannelGroup,使所有连接的 WebSocket Channel 都能接收到它。和以前一样,retain() 是必需的,因为当 channelRead0()返回时,TextWebSocketFrame 的引用计数将递减。由于所有操作都是异步的,writeAndFlush() 可能会在以后完成,我们不希望它来访问无效的引用。
由于 Netty 处理了其余大部分功能,唯一剩下的我们现在要做的是初始化 ChannelPipeline 给每一个创建的新的 Channel 。做到这一点,我们需要一个ChannelInitializer
WebsocketChatServerInitializer.java
public class WebsocketChatServerInitializer extends
ChannelInitializer&SocketChannel& {
public void initChannel(SocketChannel ch) throws Exception {//2
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(64*1024));
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpRequestHandler(&/ws&));
pipeline.addLast(new WebSocketServerProtocolHandler(&/ws&));
pipeline.addLast(new TextWebSocketFrameHandler());
1.扩展 ChannelInitializer
2.添加 ChannelHandler 到 ChannelPipeline
initChannel() 方法设置 ChannelPipeline 中所有新注册的 Channel,安装所有需要的  ChannelHandler。
WebsocketChatServer.java
编写一个 main() 方法来启动服务端。
public class WebsocketChatServer {
public WebsocketChatServer(int port) {
this.port =
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new WebsocketChatServerInitializer())
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
System.out.println(&WebsocketChatServer 启动了&);
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器
socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println(&WebsocketChatServer 关闭了&);
public static void main(String[] args) throws Exception {
if (args.length & 0) {
port = Integer.parseInt(args[0]);
port = 8080;
new WebsocketChatServer(port).run();
1. 是用来处理I/O操作的多线程事件循环器,Netty 提供了许多不同的
的实现用来处理不同的传输。在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。如何知道多少个线程已经被使用,如何映射到已经创建的 上都需要依赖于 EventLoopGroup 的实现,并且可以通过构造函数来配置他们的关系。
2. 是一个启动 NIO 服务的辅助启动类。你可以在这个服务中直接使用 Channel,但是这会是一个复杂的处理过程,在很多情况下你并不需要这样做。
3.这里我们指定使用
类来举例说明一个新的 Channel 如何接收进来的连接。
4.这里的事件处理类经常会被用来处理一个最近的已经接收的 Channel。SimpleChatServerInitializer 继承自 是一个特殊的处理类,他的目的是帮助使用者配置一个新的 Channel。也许你想通过增加一些处理类比如 SimpleChatServerHandler 来配置一个新的 Channel 或者其对应的 来实现你的网络程序。当你的程序变的复杂时,可能你会增加更多的处理类到 pipline 上,然后提取这些匿名类到最顶层的类上。
5.你可以设置这里指定的 Channel 实现的配置参数。我们正在写一个TCP/IP 的服务端,因此我们被允许设置 socket 的参数选项比如tcpNoDelay 和 keepAlive。请参考
实现的接口文档以此可以对ChannelOption 的有一个大概的认识。
6.option() 是提供给 用来接收进来的连接。childOption() 是提供给由父管道 接收到的连接,在这个例子中也是 NioServerSocketChannel。
7.我们继续,剩下的就是绑定端口然后启动服务。这里我们在机器上绑定了机器所有网卡上的 8080 端口。当然现在你可以多次调用 bind() 方法(基于不同绑定地址)。
恭喜!你已经完成了基于 Netty 聊天服务端程序。
在程序的 resources 目录下,我们创建一个 WebsocketChatClient.html 页面来作为客户端
WebsocketChatClient.html
&!DOCTYPE html&
&meta charset=&UTF-8&&
&title&WebSocket Chat&/title&
&script type=&text/javascript&&
if (!window.WebSocket) {
window.WebSocket = window.MozWebS
if (window.WebSocket) {
socket = new WebSocket(&ws://localhost:8080/ws&);
socket.onmessage = function(event) {
var ta = document.getElementById('responseText');
ta.value = ta.value + '\n' + event.data
socket.onopen = function(event) {
var ta = document.getElementById('responseText');
ta.value = &连接开启!&;
socket.onclose = function(event) {
var ta = document.getElementById('responseText');
ta.value = ta.value + &连接被关闭&;
alert(&你的浏览器不支持 WebSocket!&);
function send(message) {
if (!window.WebSocket) {
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
alert(&连接没有开启.&);
&form onsubmit=&&&
&h3&WebSocket 聊天室:&/h3&
&textarea id=&responseText& style=&width: 500 height: 300&&&/textarea&
&input type=&text& name=&message&
style=&width: 300px& value=&Welcome to &&
&input type=&button& value=&发送消息& onclick=&send(this.form.message.value)&&
&input type=&button& onclick=&javascript:document.getElementById('responseText').value=''& value=&清空聊天记录&&
&a href=&/& &更多例子请访问 &/a&
逻辑比较简单,不累述。
先运行 WebsocketChatServer,再打开多个浏览器页面实现多个 客户端访问
中 websocketchat
Netty 4.x 用户指南
Netty 实战(精髓)
怎么不用movies作为表名?不用关联movie和comments吗?
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2017 ImportNew}

我要回帖

更多关于 那年花开月正圆百度云 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信