Erlang 聊天室程序(六) 设置客户端信息2

上篇 开了个头编写了基本的框架,这次连同客户端服务器端代码一起完善下。

首先修改客户端代码:

之前在数据交换 部分,客户端中定义了一个Message bean类,里面包含了发送一条消息所需要的基本信息,包括id、type、subject、from、to、content等。但这里的

content是一个String 类型,如果要表示更复杂的消息就不太适用了。

由于所有的消息id、type、subject、from、to 这几个成员的类型是确定的,对应的操作方法也是固定的,所以我们抽象出一个抽象类:Packet 用来表示交互中的所有消息:

Packet.java:

 

package com.kinglong.socket.data; import java.util.Date; import net.sf.json.JSONObject; public abstract class Packet { String id; String from; String to; String type; String subject; Date creationDate; JSONObject json; protected Packet(){ } public Packet(JSONObject obj){ this.json=obj; } public String getId() { String theid =json.getString("id"); if(theid==null||theid.length()==0){ return null; } else{ if(id!=null||theid.equals(id)){ return id; } else{ this.id=theid; return id; } } } public void setId(String id) { if(id!=null){ this.id = id; } json.put("id", id); } public String getFrom() { String thefrom =json.getString("from"); if(thefrom==null||thefrom.length()==0){ return null; } else{ if(from!=null||thefrom.equals(id)){ return from; } else{ this.from=thefrom; return from; } } } public void setFrom(String from) { if(from!=null){ this.from = from; } json.put("from", from); } public String getTo() { String theto =json.getString("to"); if(theto==null||theto.length()==0){ return null; } else{ if(to!=null||theto.equals(to)){ return to; } else{ this.to=theto; return to; } } } public void setTo(String to) { if(to!=null) this.to = to; json.put("to", to); } public String getType() { String thetype =json.getString("type"); if(thetype==null||thetype.length()==0){ return null; } else{ if(type!=null||thetype.equals(type)){ return type; } else{ this.type=thetype; return type; } } } public void setType(String type) { if(type!=null){ this.type=type; } json.put("type", type); } public String getSubject() { String thesub =json.getString("subject"); if(thesub==null||thesub.length()==0){ return null; } else{ if(subject!=null||thesub.equals(subject)){ return subject; } else{ this.subject=thesub; return subject; } } } public void setSubject(String subject) { if(subject!=null){ this.subject = subject; } json.put("subject", subject); } public Date getCreationDate() { Date thedate =(Date)json.get("creationDate"); if(thedate==null){ return null; } else{ if(creationDate!=null||thedate.compareTo(creationDate)==0){ return creationDate; } else{ this.creationDate=thedate; return creationDate; } } } public void setCreationDate(Date creationDate) { if(creationDate!=null) this.creationDate = creationDate; json.put("creationDate", creationDate); } public String toJSON(){ return json.toString(); } } 与之前不同的是这次将Bean解析为JSON String的任务交给Bean本身。可以看到以上代码中并没有定义content的实现,所以具体的消息可根据自身需要来定制。

 

根据这个思想,改造普通消息类Message:

 

package com.kinglong.socket.data; import java.util.Date; import net.sf.json.JSONObject; import com.kinglong.util.MessageIdGenerator; public class Message extends Packet{ String content; public Message(){ this.json=new JSONObject(); } public Message(String type,String from,String to,String subject,String content){ this.json=new JSONObject(); setId(MessageIdGenerator.nextId()); setType(type); setFrom(from); setTo(to); setSubject(subject); setContent(content); setCreationDate(new Date()); } public Message(JSONObject obj){ this.json=obj; } public Message(String type){ this.json =new JSONObject(); setId(MessageIdGenerator.nextId()); setType(type); } public String getContent() { String thecon =json.getString("content"); if(thecon==null||thecon.length()==0){ return null; } else{ if(content!=null||thecon.equals(content)){ return content; } else{ this.content=thecon; return content; } } } public void setContent(String content) { if(content!=null) this.content = content; this.json.put("content", content); } public static class MessageType{ public static String MESSAGE="msg"; } public static class MessageSubject{ public static String CHAT="chat"; } } 再修改下发送部分的代码,直接调用对象的toString()方法:

 

 

public void sendMsg(Packet msg){ try { String data=msg.toJSON();//(JSONParaser.getJSON(msg)).toString(); oust.write(data.getBytes()); oust.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
新建一个类ClientInfo 表示客户端要设置的信息:

 

 

package com.kinglong.socket.data; public class ClientInfo { String nick; int sex; int age; String province; String city; int posx; int posy; public ClientInfo(String nick,int sex,int age,String province,String city,int x,int y){ this.nick=nick; this.sex=(sex>0?1:0); this.age=age; this.province=province; this.city=city; this.posx=x; this.posy=y; } public ClientInfo(){ sex=1;//default age=1; } public String getNick() { return nick; } public void setNick(String nick) { this.nick = nick; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = (sex>0?1:0); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public int getPosx() { return posx; } public void setPosx(int posx) { this.posx = posx; } public int getPosy() { return posy; } public void setPosy(int posy) { this.posy = posy; } }
新建设置客户端消息类:SetClientInfo继承Packet类

 

 

package com.kinglong.socket.data; import java.util.Date; import net.sf.json.JSONObject; public class SetClientInfo extends Packet { ClientInfo info; public SetClientInfo(){ info=new ClientInfo(); setInfo(info); setType("set"); setSubject("clientinfo"); setCreationDate(new Date()); } public SetClientInfo(String from,String to){ this(); setFrom(from); setTo(to); setType("set"); setSubject("clientinfo"); setCreationDate(new Date()); } public SetClientInfo(ClientInfo info){ this.info=info; this.json=new JSONObject(); setInfo(info); setType("set"); setSubject("clientinfo"); setCreationDate(new Date()); } public ClientInfo getInfo() { ClientInfo theinfo =(ClientInfo)json.get("content"); if(theinfo==null){ return null; } else{ if(info!=null||theinfo.equals(info)){ return info; } else{ this.info=theinfo; return info; } } } public void setInfo(ClientInfo info) { if(info!=null) this.info = info; json.put("content", info); } }
可以看到这里将content 设置为了刚才的ClientInfo 对象。

 

下面修改服务器端:

先要去掉JSON数据解析时的is_binary判断,因为以后发送的消息content里不一定就是binary了。

 

para({"content",Val},Data)-> io:format("para content:~p~n",[Data]), NewData=Data#message{content=Val}, io:format("paraed content:~p~n",[NewData]), NewData ;
再修改client_manager.erl 中更改客户端信息部分的代码,将content中的json数据转成#clientinfo,再更新到数据表中去。

 

为此新建一个专门的模块util_SetInfoParas.erl处理setClientInfo消息:

 

%% Author: Administrator %% Created: 2012-3-1 %% Description: TODO: Add description to util_SetInfoParas -module(util_SetInfoParas). %% %% Include files %% -include("clientinfo.hrl"). %% %% Exported Functions %% -export([paraElements/1]). %% %% API Functions %% %% %% Local Functions %% paraElements(Obj)-> {obj,List}=Obj, Data =#clientinfo{}, %catch exception here io:format("data list is:~p~n",[List]), try paraEle(List,Data) catch {error,Reason,NewData}-> {error,Reason,NewData} end . paraEle([Ele|Els],Data)-> io:format("ele is:~p~n",[Ele]), NewData=para(Ele,Data), paraEle(Els,NewData) ; paraEle([],Data)-> Data . para({"age",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{age=Val}, io:format("paraed content:~p~n",[NewData]), NewData ; para({"city",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{city=binary_to_list(Val)}, io:format("paraed content:~p~n",[NewData]), NewData ; para({"nick",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{nick=binary_to_list(Val)}, io:format("paraed content:~p~n",[NewData]), NewData ; para({"posx",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{posx=Val}, io:format("paraed content:~p~n",[NewData]), NewData ; para({"posy",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{posy=Val}, io:format("paraed content:~p~n",[NewData]), NewData ; para({"province",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{province=binary_to_list(Val)}, io:format("paraed content:~p~n",[NewData]), NewData ; para({"sex",Val},Data)-> io:format("para age:~p~n",[Data]), NewData=Data#clientinfo{sex=Val}, io:format("paraed content:~p~n",[NewData]), NewData ; para({Key,Val},Data)-> io:format("decode key is:~p~n",[Key]), io:format("decode Val is:~p~n",[Val]), %no mache %throw exception throw({error,"unkown element",Data}) .
接着在更新数据库前调用以上代码:

 

client_manager.erl:

 

%update clientinfo updateClient(Message)-> #message{content=Info,from=Key}=Message, io:format("content of message is:~p~n",[Info]), %TODO:we should formart content from json to record #clientinfo Rec =util_SetInfoParas:paraElements(Info), io:format("parased record is:~p~n",[Rec]), if is_record(Rec,clientinfo) -> #clientinfo{nick=Nick,sex=Sex,age=Age,province=Province, city=City,posx=Px,posy=Py}=Rec, io:format("here Key is:~p~n",[Key]), case ets:update_element(clientinfo, Key, [{#clientinfo.nick,Nick}, {#clientinfo.sex,Sex}, {#clientinfo.age,Age}, {#clientinfo.province,Province}, {#clientinfo.city,City}, {#clientinfo.posx,Px}, {#clientinfo.posy,Py}]) of true-> {ok,Rec}; false-> {fals,Rec} end; true-> io:format("wrong format of clientinfo"), {false,Rec} end . 更新成功后,会将此消息广播给所有的在线用户:

 

chat_room.erl

 

handle_call({set_clientinfo,Message},From,State)-> %we can send result message to client %or send a broadcast message to all client case client_manager:updateClient(Message)of {ok,Rec}-> #message{from=Id}=Message, #clientinfo{nick=Nick}=Rec, %Content=["client",integer_to_list(Id),"has change nickname:"|Nick], %[Cont]=["client"|integer_to_list(Id)], %io:format("Cont is:~p~n",[Cont]), %[Cont1]=[Cont|"has change nickname:"], %io:format("Cont1 is:~p~n",[Cont1]), %[Content]=[Cont1|Nick], %io:format("Content is:~p~n",[Content]), {Content}={"clinet" ++ integer_to_list(Id) ++ " has change nickname:" ++ Nick}, NewMessage=#message{id="0", from=Id, to="", content=Content, type="msg", subject="chat", time=Message#message.time}, io:format("Notice Message is:~p~n",[NewMessage]), sendMsg(NewMessage,[]); {false,Rec}-> ok end, {reply,ok,State} .
最后需要修改util_MessageParas.erl中的JSON编码部分,判断如果要发送给客户端的Message消息内容是list的话才转成相应的字符串。

 

 

%parase message to json paraseEncode(Message)-> io:format("Encoding Message:~p~n",[Message]), {message,Id,Type,From,To,Subject,Content,Time}=Message, {time,Date,Day,Hours,Minutes,Month,Seconds,TheTime,Offset,Year}=Time, %Data={obj,[{"content",list_to_binary(Content)}, TheContent= if is_list(Content)-> list_to_binary(Content); true -> Content end, Data={obj,[{"content",TheContent}, {"from",list_to_binary(From)}, {"to",list_to_binary(To)}, {"subject",list_to_binary(Subject)}, {"id",list_to_binary(Id)}, {"type",list_to_binary(Type)}, {"creationDate",{obj,[{"date",Date}, {"day",Day}, {"hours",Hours}, {"minutes",Minutes}, {"month",Month}, {"seconds",Seconds}, {"time",TheTime}, {"timezoneOffset",Offset}, {"year",Year} ] } }] }, rfc4627:encode(Data) .

 

 

再修改客户端收到消息后的解析代码:

 

@Override public void run() { // TODO Auto-generated method stub InputStreamReader reader = new InputStreamReader(inst); BufferedReader bfreader = new BufferedReader(reader); while(isrunning){ String str=null; try { byte[] data =new byte[300]; int len =0; while((len=inst.read(data))>0){ str=new String(data).trim(); Iterator<JSONObject> it =JSONParaser.getString(str); while(it.hasNext()){ JSONObject obj=it.next(); if("msg".equals(obj.get("type"))){ Message msg =(Message)JSONObject.toBean(obj,Message.class); mf.recMsg(msg); } } } } catch (IOException e) { // TODO Auto-generated catch block System.out.println("recMsg error"+e.getMessage()); isrunning=false; } try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试结果如下:

 

另外,既然能够设置客户端的昵称了,那么就再实现下发消息时from的替换吧:

先为client_manager添加获取客户端昵称功能,获取不到或undfined就取默认的:

getNick(Key)-> case ets:lookup(clientinfo, Key) of [Record]-> #clientinfo{nick=Nick}=Record, case Nick of undefined -> "client"++integer_to_list(Key); Nick-> Nick end; []-> "client"++integer_to_list(Key) end .
再修改设置个人信息后的通知消息:

 

handle_call({set_clientinfo,Message},From,State)-> %we can send result message to client %or send a broadcast message to all client #message{from=Id}=Message, [Client]=client_manager:getClient(Id), #clientinfo{pid=Pid}=Client, TheNick=client_manager:getNick(Id), case client_manager:updateClient(Message)of {ok,Rec}-> #clientinfo{nick=Nick}=Rec, %Content=["client",integer_to_list(Id),"has change nickname:"|Nick], %[Cont]=["client"|integer_to_list(Id)], %io:format("Cont is:~p~n",[Cont]), %[Cont1]=[Cont|"has change nickname:"], %io:format("Cont1 is:~p~n",[Cont1]), %[Content]=[Cont1|Nick], %io:format("Content is:~p~n",[Content]), Pid!{setinfo,Rec}, {Content}={TheNick ++ " has change nickname:" ++ Nick}, NewMessage=#message{id="0", from=Id, to="", content=Content, type="msg", subject="chat", time=Message#message.time}, io:format("Notice Message is:~p~n",[NewMessage]), sendMsg(NewMessage,[]); {false,Rec}-> ok end, {reply,ok,State} . 再修改client_session中下发时的from:

 

 

handle_info({dwmsg,Message},State)-> %here we should set from back io:format("client_session dwmsg state is ~p~n",[State]), #message{from=Id}=Message, Nick=client_manager:getNick(Id), NewMessage =Message#message{from=Nick}, JSON=util_MessageParas:paraseEncode(NewMessage), io:format("client_session dwmsg recived ~p~n",[JSON]), case gen_tcp:send(State#clientinfo.socket, JSON)of ok-> io:format("client_session dwmsg sended ~n"); {error,Reason}-> io:format("client_session dwmsg sended error ~p ~n",Reason) end, {noreply,State};
测试效果如下:

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值