Java Socket聊天室编程(二)之利用socket实现单聊聊天室
在上篇文章JavaSocket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。
其实就是建立一个一对一的聊天通讯。
与上一篇实现消息推送的代码有些不同,在它上面加以修改的。
如果没有提到的方法或者类则和上一篇一模一样。
1,修改实体类(服务器端和客户端的实体类是一样的)
1,UserInfoBean用户信息表
publicclassUserInfoBeanimplementsSerializable{
privatestaticfinallongserialVersionUID=2L;
privatelonguserId;//用户id
privateStringuserName;//用户名
privateStringlikeName;//昵称
privateStringuserPwd;//用户密码
privateStringuserIcon;//用户头像
//省略get、set方法
}
2,MessageBean聊天信息表
publicclassMessageBeanimplementsSerializable{
privatestaticfinallongserialVersionUID=1L;
privatelongmessageId;//消息id
privatelonggroupId;//群id
privatebooleanisGoup;//是否是群消息
privateintchatType;//消息类型;1,文本;2,图片;3,小视频;4,文件;5,地理位置;6,语音;7,视频通话
privateStringcontent;//文本消息内容
privateStringerrorMsg;//错误信息
privateinterrorCode;//错误代码
privateintuserId;//用户id
privateintfriendId;//目标好友id
privateMessageFileBeanchatFile;//消息附件
//省略get、set方法
}
3,MessageFileBean消息附件表
publicclassMessageFileBeanimplementsSerializable{
privatestaticfinallongserialVersionUID=3L;
privateintfileId;//文件id
privateStringfileName;//文件名称
privatelongfileLength;//文件长度
privateByte[]fileByte;//文件内容
privateStringfileType;//文件类型
privateStringfileTitle;//文件头名称
//省略get、set方法
}
2,(服务器端代码修改)ChatServer主要的聊天服务类,加以修改
publicclassChatServer{
//socket服务
privatestaticServerSocketserver;
//使用ArrayList存储所有的Socket
publicList<Socket>socketList=newArrayList<>();
//模仿保存在内存中的socket
publicMap<Integer,Socket>socketMap=newHashMap();
//模仿保存在数据库中的用户信息
publicMap<Integer,UserInfoBean>userMap=newHashMap();
publicGsongson=newGson();
/**
*初始化socket服务
*/
publicvoidinitServer(){
try{
//创建一个ServerSocket在端口8080监听客户请求
server=newServerSocket(SocketUrls.PORT);
createMessage();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
/**
*创建消息管理,一直接收消息
*/
privatevoidcreateMessage(){
try{
System.out.println("等待用户接入:");
//使用accept()阻塞等待客户请求
Socketsocket=server.accept();
//将链接进来的socket保存到集合中
socketList.add(socket);
System.out.println("用户接入:"+socket.getPort());
//开启一个子线程来等待另外的socket加入
newThread(newRunnable(){
publicvoidrun(){
//再次创建一个socket服务等待其他用户接入
createMessage();
}
}).start();
//用于服务器推送消息给用户
getMessage();
//从客户端获取信息
BufferedReaderbff=newBufferedReader(newInputStreamReader(socket.getInputStream()));
//读取发来服务器信息
Stringline=null;
//循环一直接收当前socket发来的消息
while(true){
Thread.sleep(500);
//System.out.println("内容:"+bff.readLine());
//获取客户端的信息
while((line=bff.readLine())!=null){
//解析实体类
MessageBeanmessageBean=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(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
System.out.println("错误:"+e.getMessage());
}
}
/**
*发送消息
*/
privatevoidgetMessage(){
newThread(newRunnable(){
publicvoidrun(){
try{
Stringbuffer;
while(true){
//从控制台输入
BufferedReaderstrin=newBufferedReader(newInputStreamReader(System.in));
buffer=strin.readLine();
//因为readLine以换行符为结束点所以,结尾加入换行
buffer+="\n";
//这里修改成向全部连接到服务器的用户推送消息
for(Socketsocket:socketMap.values()){
OutputStreamoutput=socket.getOutputStream();
output.write(buffer.getBytes("utf-8"));
//发送数据
output.flush();
}
}
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}).start();
}
/**
*模拟添加信息进入数据库和内存
*
*@parammessageBean
*@paramscoket
*/
privatevoidsetChatMap(MessageBeanmessageBean,Socketscoket){
//将用户信息存起来
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不同的用户信息
*
*@paramuserId
*@return
*/
privateUserInfoBeangetUserInfoBean(intuserId){
UserInfoBeanuserInfoBean=newUserInfoBean();
userInfoBean.setUserIcon("用户头像");
userInfoBean.setUserId(userId);
userInfoBean.setUserName("admin");
userInfoBean.setUserPwd("123123132a");
returnuserInfoBean;
}
/**
*将消息转发给目标好友
*
*@parammessageBean
*/
privatevoidgetFriend(MessageBeanmessageBean){
if(socketMap!=null&&socketMap.get(messageBean.getFriendId())!=null){
Socketsocket=socketMap.get(messageBean.getFriendId());
Stringbuffer=gson.toJson(messageBean);
//因为readLine以换行符为结束点所以,结尾加入换行
buffer+="\n";
try{
//向客户端发送信息
OutputStreamoutput=socket.getOutputStream();
output.write(buffer.getBytes("utf-8"));
//发送数据
output.flush();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
}
3,(客户端代码)LoginActivity登陆页面修改可以登录多人
publicclassLoginActivityextendsAppCompatActivity{
privateEditTextchat_name_text,chat_pwd_text;
privateButtonchat_login_btn;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
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(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
intstatus=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()));
Intentintent=newIntent(LoginActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
});
}
/**
*返回登陆状态,1为用户,2为另一个用户,这里模拟出两个用户互相通讯
*
*@paramname
*@parampwd
*@return
*/
privateintgetLogin(Stringname,Stringpwd){
if(TextUtils.isEmpty(name)||TextUtils.isEmpty(pwd)){
return0;//没有输入完整密码
}elseif(name.equals("admin")&&pwd.equals("1")){
return1;//用户1
}elseif(name.equals("admin")&&pwd.equals("2")){
return2;//用户2
}else{
return-1;//密码错误
}
}
/**
*实例化一个聊天服务
*
*@paramstatus
*/
privatevoidgetChatServer(intstatus){
ChatAppliaction.chatServer=newChatServer(status);
}
}
4,(客户端代码)ChatServer聊天服务代码逻辑的修改
publicclassChatServer{
privateSocketsocket;
privateHandlerhandler;
privateMessageBeanmessageBean;
privateGsongson=newGson();
//由Socket对象得到输出流,并构造PrintWriter对象
PrintWriterprintWriter;
InputStreaminput;
OutputStreamoutput;
DataOutputStreamdataOutputStream;
publicChatServer(intstatus){
initMessage(status);
initChatServer();
}
/**
*消息队列,用于传递消息
*
*@paramhandler
*/
publicvoidsetChatHandler(Handlerhandler){
this.handler=handler;
}
privatevoidinitChatServer(){
//开个线程接收消息
receiveMessage();
}
/**
*初始化用户信息
*/
privatevoidinitMessage(intstatus){
messageBean=newMessageBean();
UserInfoBeanuserInfoBean=newUserInfoBean();
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);
}elseif(status==2){//如果是用户2,那么他就指向用户1聊天
messageBean.setUserId(2);
messageBean.setFriendId(1);
}
ChatAppliaction.userInfoBean=userInfoBean;
}
/**
*发送消息
*
*@paramcontentMsg
*/
publicvoidsendMessage(StringcontentMsg){
try{
if(socket==null){
Messagemessage=handler.obtainMessage();
message.what=1;
message.obj="服务器已经关闭";
handler.sendMessage(message);
return;
}
byte[]str=contentMsg.getBytes("utf-8");//将内容转utf-8
Stringaaa=newString(str);
messageBean.setContent(aaa);
StringmessageJson=gson.toJson(messageBean);
/**
*因为服务器那边的readLine()为阻塞读取
*如果它读取不到换行符或者输出流结束就会一直阻塞在那里
*所以在json消息最后加上换行符,用于告诉服务器,消息已经发送完毕了
**/
messageJson+="\n";
output.write(messageJson.getBytes("utf-8"));//换行打印
output.flush();//刷新输出流,使Server马上收到该字符串
}catch(Exceptione){
e.printStackTrace();
Log.e("test","错误:"+e.toString());
}
}
/**
*接收消息,在子线程中
*/
privatevoidreceiveMessage(){
newThread(newRunnable(){
@Override
publicvoidrun(){
try{
//向本机的8080端口发出客户请求
socket=newSocket(SocketUrls.IP,SocketUrls.PORT);
//由Socket对象得到输入流,并构造相应的BufferedReader对象
printWriter=newPrintWriter(socket.getOutputStream());
input=socket.getInputStream();
output=socket.getOutputStream();
dataOutputStream=newDataOutputStream(socket.getOutputStream());
//从客户端获取信息
BufferedReaderbff=newBufferedReader(newInputStreamReader(input));
//读取发来服务器信息
Stringline;
while(true){
Thread.sleep(500);
//获取客户端的信息
while((line=bff.readLine())!=null){
Log.i("socket","内容:"+line);
MessageBeanmessageBean=gson.fromJson(line,MessageBean.class);
Messagemessage=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(Exceptione){
e.printStackTrace();
Log.e("test","错误:"+e.toString());
}
}
}).start();
}
publicSocketgetSocekt(){
if(socket==null)returnnull;
returnsocket;
}
}
如此一来,代码逻辑已经从消息推送的逻辑修改成了单聊的逻辑了。
这个代码可以让用户1和用户2相互聊天,并且服务器会记录下他们之间的聊天记录。并且服务器还是拥有消息推送的功能。
以上所述是小编给大家介绍的JavaSocket聊天室编程(二)之利用socket实现单聊聊天室,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!