公司最近在开发一个移动端的聊天室,c#写的服务端。android 和ios 的客户端,这里用到的socket 通讯,但是socket 的io阻塞导致数据沾包,里一个小伙写了一个解决沾包的问题,但是我看了不是太理想,只有这个项目可以使用,换了其他项目就不能用了。偶然发现netty 这个东西。网上关于这个的资料不多,都是服务端的,android 这边几乎没有发现有用的资料。然后自己写了一个,现在贴出来大家看看,有问题可以交流下。
Email : ngh8897@163.com
废话不多说,直接上代码
MainActivity.java 主页面
import java.net.InetSocketAddress;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String TAG = "TAG";
public static int MSG_REC = 0xabc;
public static final String HOST = "192.168.2.219";
public static int PORT = 9103;
private NioEventLoopGroup group;
private Channel mChannel;
private ChannelFuture cf;
private static Context context;
private TextView tv;
private Button btn;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_REC) {
Toast.makeText(context, msg.obj.toString(), 0).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
connected();
tv = (TextView) findViewById(R.id.iv);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
sendMessage();
}
});
}
// 连接到Socket服务端
private void connected() {
new Thread() {
@Override
public void run() {
group = new NioEventLoopGroup();
try {
// Client服务启动器 3.x的ClientBootstrap
// 改为Bootstrap,且构造函数变化很大,这里用无参构造。
Bootstrap bootstrap = new Bootstrap();
// 指定EventLoopGroup
bootstrap.group(group);
// 指定channel类型
bootstrap.channel(NioSocketChannel.class);
// 指定Handler
bootstrap
.handler(new MyClientInitializer(MainActivity.this));
//如果没有数据,这个可以注释看看
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// 连接到本地的7878端口的服务端
cf = bootstrap.connect(new InetSocketAddress(HOST, PORT));
mChannel = cf.sync().channel();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
// 发送数据
private void sendMessage() {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
Log.i(TAG, "mChannel.write sth & " + mChannel.isOpen());
mChannel.writeAndFlush("我是android客户端");
mChannel.read();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (group != null) {
group.shutdownGracefully();
}
}
}
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class MyClientHandler extends SimpleChannelInboundHandler<String> {
private Context context;
public MyClientHandler(Context context) {
this.context = context;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
Log.d("MyHelloClientHandler", "channelRead0->msg=" + msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client active");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client close ");
super.channelInactive(ctx);
}
}
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
private Context context;
public MyClientInitializer(Context ctx) {
this.context = ctx;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/**
* 这个地方的必须和服务端对应上。否则无法正常解码和编码
*/
// pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192,
// Delimiters.lineDelimiter())); //这个是解决沾包的问题,但是我发现加上这句话,就读取不到返回值,不知道为什么
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
// 客户端的逻辑
pipeline.addLast("handler", new MyClientHandler(context));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("---channelRead--- msg=" + msg);
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("---channelReadComplete---");
super.channelReadComplete(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Log.i("MyClientInitializer", "---channelActive---");
super.channelActive(ctx);
}
}
既然是网络通信,肯定得加上网络权限
要加上netty4 的jar
下载地址:http://netty.io/downloads.html