@Tyhj
2017-02-24T20:50:16.000000Z
字数 8015
阅读 2004
Java
最近想搞一下后台,所以socket是肯定要看的,之前也看过一点点Mina,感觉还可以,所以现在来做一下,当然是做聊天服务器啦。
到现在发现做软件先想好,把文档写一下还是不错的,这样也挺轻松的。
- mina-core-2.0.16.jar
- slf4j-api-1.7.21.jar
- slf4j-nop-1.7.21.jar
- org.json.jar
NioSocketAcceptor acceptor=new NioSocketAcceptor();
acceptor.setHandler(new MySeverHandler());
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MyTextLineFactory()));
////设置服务器空闲状态̬
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
acceptor.bind(new InetSocketAddress(9897));
acceptor.getManagedSessions();
可以自己设定逻辑,重点在于,客户端第一次建立连接的时候,要发送一个设置自己的身份,标识的信息,然后在服务器端接收到,就给每个IoSession设置身份,然后就好办了。
客户端建立连接的时候发送一个身份给服务器
@Override
public void sessionCreated(IoSession session) throws Exception {
// TODO Auto-generated method stub
System.out.println("已建立连接" + session.getRemoteAddress());
session.write(getJson.getMsg(id, null, null, null));
}
服务器接收到消息,判断该IoSession有没有设置身份,没有就设置,否则就进行消息传送
JSONObject jsonObject=new JSONObject((String)message);
String from=(String) session.getAttribute("from","xx");
if(from.equals("xx")){
from=jsonObject.getString("from");
if(from!=null){
session.setAttribute("from",from);
}
}else{
//System.out.println("messageReceived:"+(String)message+session.getAttribute("from","xx"));
//传达消息
new Msg().send2One(session, jsonObject);
}
接收到消息后解析一下,看一下是发给谁的,然后找到所有的IoSession,看看是发给哪一个,然后用这个IoSession发送出去
记得消息最后面要加换行符"\n"
Collection<IoSession> sessions = session.getService().getManagedSessions().values();
for (IoSession sess : sessions) {
if (sess.getAttribute("from", "xx").equals(jsonObject.getString("to")))
sess.write(jsonObject.toString()+"\n");
}
服务器端监听消息类,还有几个什么格式转化的还是什么类,也是必须的,就懒得贴了
public class MySeverHandler extends IoHandlerAdapter{
@Override
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
System.out.println("exceptionCaught"+cause.getMessage());
}
//收到消息
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
JSONObject jsonObject=new JSONObject((String)message);
String from=(String) session.getAttribute("from","xx");
if(from.equals("xx")){
from=jsonObject.getString("from");
if(from!=null){
session.setAttribute("from",from);
}
}else{
//System.out.println("messageReceived:"+(String)message+session.getAttribute("from","xx"));
//传达消息
new Msg().send2One(session, jsonObject);
}
}
//发消息时候
@Override
public void messageSent(IoSession session, Object message) throws Exception {
System.out.println("messageSent");
}
@Override
public void sessionClosed(IoSession session) throws Exception {
session.close();
System.out.println("sessionClosed");
}
@Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("sessionCreated");
}
//客户端空闲时候
@Override
public void sessionIdle(IoSession session, IdleStatus status)
throws Exception {
//System.out.println("sessionIdle");
}
@Override
public void sessionOpened(IoSession session) throws Exception {
System.out.println("sessionOpened");
}
}
同样的那几个包导进来,然后就开始连接,连接需要,ip和端口,端口是刚才服务器那里自己设置的,我的是9897,然后连接完了得到了session,就可以用来发送消息了。同样有个消息监听类,就不贴了,
//Create TCP/IP connection
connector=new NioSocketConnector();
//创建接受数据的过滤器
DefaultIoFilterChainBuilder chain = connector.getFilterChain();
//设定这个过滤器将一行一行(/r/n)的读取数据
chain.addLast("myChin", new ProtocolCodecFilter(new TextLineCodecFactory()));
MinaClientHandler minaClientHandler=new MinaClientHandler();
minaClientHandler.setId(id);
//客户端的消息处理器:一个SamplMinaServerHander对象
connector.setHandler(minaClientHandler);
//set connect timeout
connector.setConnectTimeout(30);
//连接到服务器:
ConnectFuture cf = connector.connect(new InetSocketAddress(ip,port));
//Wait for the connection attempt to be finished.
cf.awaitUninterruptibly();
session=cf.getSession();
session.write("msg");
这样子搞,自己设计一下,就可以发送消息给指定的人,网上根本找不到,全是我连蒙带猜搞出来的,所以我不知道这方法是不是很好,能用就好。
其实关键在于变量session,对于服务器:每个用户连接进来都是要新建一个session,通过它可以设置身份标示,可以获取所有的session,可以发送消息给这个session所代表的用户;对于客户端:可以用于发送请求和接收返回值。
发送消息可以发送了,那离线消息肯定也要,就需要判断用户是否在线,一般的退出我们可以控制,可以告诉服务器我的账号离线了,但是突然断网这种怎么办呢。
其实在服务器的监听类中有个方法,messageSent();就是发送成功的时候有反馈,不成功的话就是对方离线了。
当messageSent()监听到消息发送成功的时候,我们获取消息的id,然后将数据库中的相应的m_isRead字段改为1即为已接收。
我们需要知道登录注册是否成功,就是客户端接收到这些请求,在执行完成后发送消息给客户端,客户端接收到后把返回值保存,我用一个循环去读取这个值,当不为null,或者时间超出2000毫秒的时候返回。
断线就是服务器和客户端的连接断了,可能是手机突然断网了,可能是服务器出问题了,或者其他,但是只要连接断了,客户端监听类里面的sessionClosed方法都会监听到,所以只要在里面写好重连方法就好了。
@Override
public void sessionClosed(final IoSession session) throws Exception {
System.out.println("客户端与服务端断开连接.....");
new Thread(new Runnable() {
@Override
public void run() {
while (!connect.reconnect().isConnected()) {
try {
Thread.sleep(2000);
System.out.println("正在尝试重新连接.....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
重连的时候需要重新连接到服务器并且重新设置自己的身份,而连接服务器时候一些变量已经初始化了,如果重复初始化会出现一些问题,会报错。之前初始化的方法,大概可以改成这个样子:
if(session==null) {
connector = new NioSocketConnector();
//创建接受数据的过滤器
DefaultIoFilterChainBuilder chain = connector.getFilterChain();
//设定这个过滤器将一行一行(/r/n)的读取数据
chain.addLast("myChin", new ProtocolCodecFilter(new TextLineCodecFactory()));
minaClientHandler = new MinaClientHandler(this);
//客户端的消息处理器:一个SamplMinaServerHander对象
connector.setHandler(minaClientHandler);
//set connect timeout
connector.setConnectTimeout(5);
}
//连接到服务器:
ConnectFuture cf = connector.connect(new InetSocketAddress(ip,port));
//Wait for the connection attempt to be finished.
cf.awaitUninterruptibly();
try {
session = cf.getSession();
}catch (Exception e){
System.out.println("服务器无响应");
}
} catch (Exception e) {
e.printStackTrace();
}
设置身份其实只要告诉服务器,我现在是重连操作,并且把我的身份标识放到session里面,然后把之前那个断线之前的session删除掉就好了。
getInstance(ip,port);
JSONObject jsonObject=new JSONObject();
try {
jsonObject.put("action","reconnect");
jsonObject.put("id",u_id);
session.write(jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
return session;
当然这样写,就是如果断线了会一直重新尝试连接直到连接上服务器为止,想改进的话可以通过监听网络变化来操作。
在使用mina框架时候,传输的数据太多的话会出错,因为数据长度有限制。
解决方法:
//之前客户端的代码改一下
TextLineCodecFactory textLineCodecFactory=new TextLineCodecFactory();
//在这里设置限制长度
textLineCodecFactory.setDecoderMaxLineLength(102400);
textLineCodecFactory.setEncoderMaxLineLength(102400);
//设定这个过滤器将一行一行(/r/n)的读取数据
chain.addLast("myChin", new ProtocolCodecFilter(textLineCodecFactory));
User/用户信息
字段 | 数据类型 | 备注 |
---|---|---|
u_id | string | 用户id |
u_name | string | 昵称 |
u_pwd | string | 登录密码 |
u_email | string | 邮箱 |
head_image | string | 头像 |
signature | string | 签名 |
u_tags | string | 标签 |
sex | string | 性别 |
create table user(
u_id nvarchar(50) primary key,
u_name nvarchar(50),
u_pwd nvarchar(100),
u_email nvarchar(100),
u_tags text,
head_image nvarchar(100),
signature nvarchar(100),
sex nvarchar(50)
)default charset=utf8;
Msg/消息记录
字段 | 数据类型 | 备注 |
---|---|---|
m_id | string | id |
m_belong | string | 归属 |
m_msg | string/json | 信息内容 |
m_to | string | 接收者 |
m_date | DATETIME | 发送时间 |
m_isRead | int | 是否接收 |
create table msg(
m_id nvarchar(100) primary key,
m_belong nvarchar(100),
m_msg text,
m_to nvarchar(100),
m_date datetime,
m_isReceive integer
)default charset=utf8;
Group_user/群
字段 | 数据类型 | 备注 |
---|---|---|
g_id | string | 群id |
u_id | string | 用户id |
jsonObject.put("action",action);
jsonObject.put("id",now+from+to);
jsonObject.put("from",from);
jsonObject.put("to",to);
jsonObject.put("msg",msg);
jsonObject.put("date",now.toString());
msg:
jsonObject.put("msg",msg);
jsonObject.put("type",type);
jsonObject.put("length",length);
{"msg":
{"msg":"嗨","length":0,"type":0},
"date":"2016-12-13 10:39:24.426",
"action":"msgSent",
"from":"Tyhj5",
"id":"2016-12-13 10:39:24.426Tyhj5Tyhj",
"to":"Tyhj"
}
jsonObject.put("action","signIn");
jsonObject.put("email",email);
jsonObject.put("pwd",pwd);
{"action":"signIn","email":"Tyhj5","pwd":"4444"}
jsonObject.put("action","signUp")
jsonObject.put("name",name)
jsonObject.put("email",email)
jsonObject.put("pwd",pwd);
{"action":"signUp","name":"tyhj5","email":"Tyhj5","pwd":"4444"}
jsonObject.put("code", code);
jsonObject.put("action", action);
jsonObject.put("msg", GetCode.getCode(code));
{"msg":"邮箱已被注册","code":204,"action":"signUp"}
jsonObject.put("action","reconnect");
jsonObject.put("id",u_id);
{"action":"reconnect","id":"Tyhj"}
object2.put("u_id", rs.getString(1));
object2.put("u_name", rs.getString(2));
object2.put("head_image", rs.getString(6));
object2.put("signature", rs.getString(7));
{"msg":{"friends":[{"u_name":"i_tyhj@163.com","head_image":"null","u_id":"i_tyhj@163.com2016-12-19 17:28:25.641","signature":"null"}]},"code":200,"action":"getFriends"}
//请求
jsonObject.put("action","getNewMsg");
jsonObject.put("to",User.userInfo.getId());
//返回值