当前位置: 移动技术网 > IT编程>开发语言>Java > Java Socket聊天室编程(二)之利用socket实现单聊聊天室

Java Socket聊天室编程(二)之利用socket实现单聊聊天室

2019年07月22日  | 移动技术网IT编程  | 我要评论

在上篇文章java socket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。

其实就是建立一个一对一的聊天通讯。

与上一篇实现消息推送的代码有些不同,在它上面加以修改的。

如果没有提到的方法或者类则和上一篇一模一样。

1,修改实体类(服务器端和客户端的实体类是一样的)

1,userinfobean 用户信息表

public class userinfobean implements serializable {
private static final long serialversionuid = 2l;
private long userid;// 用户id
private string username;// 用户名
private string likename;// 昵称
private string userpwd;// 用户密码
private string usericon;// 用户头像
//省略get、set方法
}

2,messagebean 聊天信息表

public class messagebean implements serializable {
private static final long serialversionuid = 1l;
private long messageid;// 消息id
private long groupid;// 群id
private boolean isgoup;// 是否是群消息
private int chattype;// 消息类型;1,文本;2,图片;3,小视频;4,文件;5,地理位置;6,语音;7,视频通话
private string content;// 文本消息内容
private string errormsg;// 错误信息
private int errorcode;// 错误代码
private int userid;//用户id
private int friendid;//目标好友id
private messagefilebean chatfile;// 消息附件
//省略get、set方法
}

3,messagefilebean 消息附件表

public class messagefilebean implements serializable {
private static final long serialversionuid = 3l;
private int fileid;//文件id
private string filename;//文件名称
private long filelength;//文件长度
private byte[] filebyte;//文件内容
private string filetype;//文件类型
private string filetitle;//文件头名称
//省略get、set方法
}

2,(服务器端代码修改)chatserver 主要的聊天服务类,加以修改

public class chatserver {
// socket服务
private static serversocket server;
// 使用arraylist存储所有的socket
public list<socket> socketlist = new arraylist<>();
// 模仿保存在内存中的socket
public map<integer, socket> socketmap = new hashmap();
// 模仿保存在数据库中的用户信息
public map<integer, userinfobean> usermap = new hashmap();
public gson gson = new gson();
/**
* 初始化socket服务
*/
public void initserver() {
try {
// 创建一个serversocket在端口8080监听客户请求
server = new serversocket(socketurls.port);
createmessage();
} catch (ioexception e) {
// todo auto-generated catch block
e.printstacktrace();
}
}
/**
* 创建消息管理,一直接收消息
*/
private void createmessage() {
try {
system.out.println("等待用户接入 : ");
// 使用accept()阻塞等待客户请求
socket socket = server.accept();
// 将链接进来的socket保存到集合中
socketlist.add(socket);
system.out.println("用户接入 : " + socket.getport());
// 开启一个子线程来等待另外的socket加入
new thread(new runnable() {
public void run() {
// 再次创建一个socket服务等待其他用户接入
createmessage();
}
}).start();
// 用于服务器推送消息给用户
getmessage();
// 从客户端获取信息
bufferedreader bff = new bufferedreader(new inputstreamreader(socket.getinputstream()));
// 读取发来服务器信息
string line = null;
// 循环一直接收当前socket发来的消息
while (true) {
thread.sleep(500);
// system.out.println("内容 : " + bff.readline());
// 获取客户端的信息
while ((line = bff.readline()) != null) {
// 解析实体类
messagebean messagebean = gson.fromjson(line, messagebean.class);
// 将用户信息添加进入map中,模仿添加进数据库和内存
// 实体类存入数据库,socket存入内存中,都以用户id作为参照
setchatmap(messagebean, socket);
// 将用户发送进来的消息转发给目标好友
getfriend(messagebean);
system.out.println("用户 : " + usermap.get(messagebean.getuserid()).getusername());
system.out.println("内容 : " + messagebean.getcontent());
}
}
// server.close();
} catch (exception e) {
// todo auto-generated catch block
e.printstacktrace();
system.out.println("错误 : " + e.getmessage());
}
}
/**
* 发送消息
*/
private void getmessage() {
new thread(new runnable() {
public void run() {
try {
string buffer;
while (true) {
// 从控制台输入
bufferedreader strin = new bufferedreader(new inputstreamreader(system.in));
buffer = strin.readline();
// 因为readline以换行符为结束点所以,结尾加入换行
buffer += "\n";
// 这里修改成向全部连接到服务器的用户推送消息
for (socket socket : socketmap.values()) {
outputstream output = socket.getoutputstream();
output.write(buffer.getbytes("utf-8"));
// 发送数据
output.flush();
}
}
} catch (ioexception e) {
// todo auto-generated catch block
e.printstacktrace();
}
}
}).start();
}
/**
* 模拟添加信息进入数据库和内存
* 
* @param messagebean
* @param scoket
*/
private void setchatmap(messagebean messagebean, socket scoket) {
// 将用户信息存起来
if (usermap != null && usermap.get(messagebean.getuserid()) == null) {
usermap.put(messagebean.getuserid(), getuserinfobean(messagebean.getuserid()));
}
// 将对应的链接进来的socket存起来
if (socketmap != null && socketmap.get(messagebean.getuserid()) == null) {
socketmap.put(messagebean.getuserid(), scoket);
}
}
/**
* 模拟数据库的用户信息,这里创建id不同的用户信息
* 
* @param userid
* @return
*/
private userinfobean getuserinfobean(int userid) {
userinfobean userinfobean = new userinfobean();
userinfobean.setusericon("用户头像");
userinfobean.setuserid(userid);
userinfobean.setusername("admin");
userinfobean.setuserpwd("123123132a");
return userinfobean;
}
/**
* 将消息转发给目标好友
* 
* @param messagebean
*/
private void getfriend(messagebean messagebean) {
if (socketmap != null && socketmap.get(messagebean.getfriendid()) != null) {
socket socket = socketmap.get(messagebean.getfriendid());
string buffer = gson.tojson(messagebean);
// 因为readline以换行符为结束点所以,结尾加入换行
buffer += "\n";
try {
// 向客户端发送信息
outputstream output = socket.getoutputstream();
output.write(buffer.getbytes("utf-8"));
// 发送数据
output.flush();
} catch (ioexception e) {
// todo auto-generated catch block
e.printstacktrace();
}
}
}
}

3,(客户端代码)loginactivity 登陆页面修改可以登录多人

public class loginactivity extends appcompatactivity {
private edittext chat_name_text, chat_pwd_text;
private button chat_login_btn;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_login);
chat_name_text = (edittext) findviewbyid(r.id.chat_name_text);
chat_pwd_text = (edittext) findviewbyid(r.id.chat_pwd_text);
chat_login_btn = (button) findviewbyid(r.id.chat_login_btn);
chat_login_btn.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
int status = getlogin(chat_name_text.gettext().tostring().trim(), chat_pwd_text.gettext().tostring().trim());
if (status == -1 || status == 0) {
toast.maketext(loginactivity.this, "密码错误", toast.length_long).show();
return;
}
getchatserver(getlogin(chat_name_text.gettext().tostring().trim(), chat_pwd_text.gettext().tostring().trim()));
intent intent = new intent(loginactivity.this, mainactivity.class);
startactivity(intent);
finish();
}
});
}
/**
* 返回登陆状态,1为用户,2为另一个用户,这里模拟出两个用户互相通讯
*
* @param name
* @param pwd
* @return
*/
private int getlogin(string name, string pwd) {
if (textutils.isempty(name) || textutils.isempty(pwd)) {
return 0;//没有输入完整密码
} else if (name.equals("admin") && pwd.equals("1")) {
return 1;//用户1
} else if (name.equals("admin") && pwd.equals("2")) {
return 2;//用户2
} else {
return -1;//密码错误
}
}
/**
* 实例化一个聊天服务
*
* @param status
*/
private void getchatserver(int status) {
chatappliaction.chatserver = new chatserver(status);
}
}

4,(客户端代码)chatserver 聊天服务代码逻辑的修改

public class chatserver {
private socket socket;
private handler handler;
private messagebean messagebean;
private gson gson = new gson();
// 由socket对象得到输出流,并构造printwriter对象
printwriter printwriter;
inputstream input;
outputstream output;
dataoutputstream dataoutputstream;
public chatserver(int status) {
initmessage(status);
initchatserver();
}
/**
* 消息队列,用于传递消息
*
* @param handler
*/
public void setchathandler(handler handler) {
this.handler = handler;
}
private void initchatserver() {
//开个线程接收消息
receivemessage();
}
/**
* 初始化用户信息
*/
private void initmessage(int status) {
messagebean = new messagebean();
userinfobean userinfobean = new userinfobean();
userinfobean.setuserid(2);
messagebean.setmessageid(1);
messagebean.setchattype(1);
userinfobean.setusername("admin");
userinfobean.setuserpwd("123123123a");
//以下操作模仿当用户点击了某个好友展开的聊天界面,将保存用户id和聊天目标用户id
if (status == 1) {//如果是用户1,那么他就指向用户2聊天
messagebean.setuserid(1);
messagebean.setfriendid(2);
} else if (status == 2) {//如果是用户2,那么他就指向用户1聊天
messagebean.setuserid(2);
messagebean.setfriendid(1);
}
chatappliaction.userinfobean = userinfobean;
}
/**
* 发送消息
*
* @param contentmsg
*/
public void sendmessage(string contentmsg) {
try {
if (socket == null) {
message message = handler.obtainmessage();
message.what = 1;
message.obj = "服务器已经关闭";
handler.sendmessage(message);
return;
}
byte[] str = contentmsg.getbytes("utf-8");//将内容转utf-8
string aaa = new string(str);
messagebean.setcontent(aaa);
string messagejson = gson.tojson(messagebean);
/**
* 因为服务器那边的readline()为阻塞读取
* 如果它读取不到换行符或者输出流结束就会一直阻塞在那里
* 所以在json消息最后加上换行符,用于告诉服务器,消息已经发送完毕了
* */
messagejson += "\n";
output.write(messagejson.getbytes("utf-8"));// 换行打印
output.flush(); // 刷新输出流,使server马上收到该字符串
} catch (exception e) {
e.printstacktrace();
log.e("test", "错误:" + e.tostring());
}
}
/**
* 接收消息,在子线程中
*/
private void receivemessage() {
new thread(new runnable() {
@override
public void run() {
try {
// 向本机的8080端口发出客户请求
socket = new socket(socketurls.ip, socketurls.port);
// 由socket对象得到输入流,并构造相应的bufferedreader对象
printwriter = new printwriter(socket.getoutputstream());
input = socket.getinputstream();
output = socket.getoutputstream();
dataoutputstream = new dataoutputstream(socket.getoutputstream());
// 从客户端获取信息
bufferedreader bff = new bufferedreader(new inputstreamreader(input));
// 读取发来服务器信息
string line;
while (true) {
thread.sleep(500);
// 获取客户端的信息
while ((line = bff.readline()) != null) {
log.i("socket", "内容 : " + line);
messagebean messagebean = gson.fromjson(line, messagebean.class);
message message = handler.obtainmessage();
message.obj = messagebean.getcontent();
message.what = 1;
handler.sendmessage(message);
}
if (socket == null)
break;
}
output.close();//关闭socket输出流
input.close();//关闭socket输入流
socket.close();//关闭socket
} catch (exception e) {
e.printstacktrace();
log.e("test", "错误:" + e.tostring());
}
}
}).start();
}
public socket getsocekt() {
if (socket == null) return null;
return socket;
}
}

如此一来,代码逻辑已经从消息推送的逻辑修改成了单聊的逻辑了。

这个代码可以让用户1和用户2相互聊天,并且服务器会记录下他们之间的聊天记录。并且服务器还是拥有消息推送的功能。

以上所述是小编给大家介绍的java socket聊天室编程(二)之利用socket实现单聊聊天室,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网