转自:http://ovalpo.info/?p=362
学了Erlang三个礼拜,做了这个程序,费了4天时间,这四天查阅了wxwidgts,wxErlang,等界面设计的资料,英文还是不行 呀!今天被老大说我的程序使用的协议不规范。我想这算个RC版吧,哈哈。本程序使用Erlang + wxErlang + mysql。Erlang这东西不火,也是个麻烦,Web上Erlang的资料可怜地少,先贴个效果图吧。
这个程序:不小心搞了个变量去匹配消息,然后变量不变这个害我好惨。记住:Erlang变量特性,变量不变
程序很拙劣,但我还是想贴出来,以示我Erlang入门了,哈哈。
客户端:client.erl 服务端:server.erl
程序:在线多人聊天系统
服务器端代码:
-module(server).
-export([start/0]).
% 启动一个服务器
start() ->
case gen_tcp:listen(1110,[binary,{packet,0},{reuseaddr,true},{active,true}]) of
{ok,Listen} ->
spawn(fun() -> par_connect(Listen) end);
{error,Why} ->
io:format(“server start error,the reason maybe ~p~n
now is going restart~n“,[Why]),
end.
% 建立链接
start()
par_connect(Listen) ->
case gen_tcp:accept(Listen) of
{ok,Socket} ->
spawn(fun() -> par_connect(Listen) end),
loop(Socket);
{error,Why} ->
io:format(“create internet connect failed,the reasom maybe ~p~n
now is going reconnect~n“,[Why]),
par_connect(Listen)
end.
% 监听消息
loop(Socket) ->
receive
{tcp,Socket,Bin} ->
case binary_to_term(Bin) of
{login,Username,Password} -> % 请求登入聊天系统
handleLoginRequest(Username,Password,Socket),
loop(Socket);
{socket,ChatRoom} -> % 用户选择了聊天房间
handleChoseRoom(ChatRoom,Socket),
loop(Socket);
{msg,ChatRoom,Username,Msg} -> % 用户发送聊天信息
handleChatMsg(ChatRoom,Username,Msg),
loop(Socket);
{totalusers,ChatRoom} -> % 用户请求在线总人数
handleTotalUsersRequest(ChatRoom,Socket),
loop(Socket);
{logintimes,Username} -> % 用户请求登入系统次数
handleLoginTimesRequest(Username,Socket),
loop(Socket);
{chattimes,Username} -> % 用户请求聊天次数
handleChatTimesRequest(Username,Socket),
loop(Socket);
{lastlogin,Username} -> % 用户请求最近一次登陆系统时间
handleLastLoginRequest(Username,Socket),
loop(Socket)
end;
{tcp_closed,Socket} -> % 用户登出聊天系统
handleTcpClosed(Socket),
loop(Socket)
end.
% 请求登入聊天系统
handleLoginRequest(Username,Password,Socket) ->
mysql:start_link(chat,“localhost”,“root”,“weihua0620″,“test”), % 链接数据库
case checkAccount(Username,Password) of % 检测用户名,密码是否合法
ok -> RoomList = getChatRoomList(); % 合法,获取聊天房间
nok -> RoomList = [] % 不合法,返回空聊天房间
end,
% 向客户端发送聊天房间列表
gen_tcp:send(Socket,term_to_binary({roomlist,RoomList})),
% 登入成功 更新登入次数 和 最后一次登陆时间
case RoomList of
[] -> loginfailed;
_ ->
Sql1 = lists:concat(["select login_times from account
where username = '",Username,"'"]),
case mysql:fetch(chat,Sql1) of % 提取登入次数
{data,Result} ->
[[Logintimes]] = mysql:get_result_rows(Result),
Sql2 = lists:concat(["update account set login_times = ",
Logintimes+1," where username = '",Username,"'"]), mysql:fetch(chat,Sql2); % 更新登入次数
{error,_Result} -> no
end,
Sql3 = lists:concat(["update account set last_login = now()
where username = '",Username,"'"]), mysql:fetch(chat,Sql3) % 更新最近一次登入时间
end.
% 检查用户名,密码是否正确
checkAccount(Username,Password) ->
Sql = lists:concat(["select * from account where username = '",
Username,"'and password = '",Password,"'"]),
case mysql:fetch(chat,Sql) of
{data,Result} ->
case mysql:get_result_rows(Result) of
[] -> nok; % 用户名,密码有误
_ -> ok % 用户名,密码有效
end;
{error,_Result} ->
checkAccount(Username,Password)
end.
% 获取聊天房间列表
getChatRoomList() ->
Sql = “select roomname from chatroom” ,
case mysql:fetch(chat,Sql) of
{data,Result} ->
case mysql:get_result_rows(Result) of
[] -> [];
RoomList -> binaryToList(RoomList) % 处理提取出来的二进制数据
end;
{error,_Result} ->
getChatRoomList()
end.
binaryToList([]) -> [];
binaryToList([[H]|T]) ->
[binary_to_list(H)|binaryToList(T)].
% 用户选择了聊天房间,保存当前 Socket handleChoseRoom(ChatRoom,Socket) ->
Soc = binary_to_list(base64:encode(term_to_binary(Socket))),
Sql = lists:concat(["insert into roomsocket values('",ChatRoom,"','",Soc,"')"]),
mysql:fetch(chat,Sql).
% 用户发送聊天信息
handleChatMsg(ChatRoom,Username,Msg) ->
% 更新用户聊天次数,每发一条聊天 msg ,聊天次数 +1
Sql = lists:concat(
["select chat_times from account where username = '",Username,"'"] ),
case mysql:fetch(chat,Sql) of
{data,Result} ->
[[Chattimes]] = mysql:get_result_rows(Result),
Sql2 = lists:concat(["update account set chat_times = ",Chattimes+1,
" where username = '",Username,"'"]),
mysql:fetch(chat,Sql2);
{error,_Result} -> no
end,
% 在聊天房间广播用户Username 的 Msg
sendRoomMsg(ChatRoom,Username,Msg).
% 向聊天房间广播用户 Username 的消息 Msg sendRoomMsg(ChatRoom,Username,Msg) ->
Sql = lists:concat(
["select socket from roomsocket where roomname = '",ChatRoom,"'"] ),
case mysql:fetch(chat,Sql) of
{data,Result} ->
case mysql:get_result_rows(Result) of
[] -> nosocket;
SocketList ->
Sockets = binaryToList(SocketList), % 处理提取出来的二进制数据
sendMessage(Username,Sockets,Msg)
end;
{error,_Result} ->
sendRoomMsg(ChatRoom,Username,Msg)
end.
% 向房间内的所有用户(socket识别)广播消息 sendMessage(_Username,[],_Msg) ->[]; sendMessage(Username,Sockets,Msg) ->
[H|T] = Sockets,
Socket = binary_to_term(base64:decode(H)),
gen_tcp:send(Socket,term_to_binary({msg,Username,Msg})),
sendMessage(Username,T,Msg).
% 用户请求在线总人数
handleTotalUsersRequest(ChatRoom,Socket) ->
Sql = lists:concat(
["select count(socket) from roomsocket where roomname = '",ChatRoom,"'"]),
case mysql:fetch(chat,Sql) of
{data,Result} ->
[[Num]] = mysql:get_result_rows(Result),
gen_tcp:send(Socket,term_to_binary({totalusers,Num}));
{error,_Result} -> no
end.
% 用户请求登入系统次数
handleLoginTimesRequest(Username,Socket) ->
Sql = lists:concat(
["select login_times from account where username = '",Username,"'"]),
case mysql:fetch(chat,Sql) of
{data,Result} ->
[[LoginTimes]] = mysql:get_result_rows(Result),
gen_tcp:send(Socket,term_to_binary({logintimes,LoginTimes}));
{error,_Result} -> no
end.
% 用户请求聊天次数
handleChatTimesRequest(Username,Socket) ->
Sql = lists:concat(
["select chat_times from account where username = '",Username,"'"]),
case mysql:fetch(chat,Sql) of
{data,Result} ->
[[ChatTimes]] = mysql:get_result_rows(Result),
gen_tcp:send(Socket,term_to_binary({chattimes,ChatTimes}));
{error,_Result} ->no
end.
% 用户请求最近一次登陆系统时间
handleLastLoginRequest(Username,Socket) ->
Sql = lists:concat(
["select last_login from account where username = '",Username,"'"]),
case mysql:fetch(chat,Sql) of
{data,Result} ->
[[{ datetime,
{{Year,Month,Day},
{Hour,Minute,Second}} }]] =mysql:get_result_rows(Result), LastLoginTime = lists:concat(
[Year,":",Month,":",Day," ",Hour,":",Minute,":",Second] ), gen_tcp:send(Socket,term_to_binary({lastlogin,LastLoginTime}));
{error,_Result} ->no
end.
% 用户退出了聊天系统,删除用户的在数据库中的 socket记录
handleTcpClosed(Socket) ->
Soc = binary_to_list(base64:encode(term_to_binary(Socket))),
Sql = lists:concat(["delete from roomsocket where socket = '",Soc,"'"]),
mysql:fetch(chat,Sql).
客户端代码:
-module(client).
-include_lib(“wx/include/wx.hrl”).
-export([start/0]). | ||
-define(ABOUT,8110). | % |
关于系统 |
-define(ONLINEUSERNUM,8111). | % |
在线用户数(同一个聊天房间) |
-define(LOGINTIMES,8112). | % |
用户登入次数 |
-define(CHATTIMES,8113). | % |
用户聊天次数(没法送一条 msg,聊天次数 +1) |
-define(MYACCOUNT,8114). | % |
用户最后一次登入时间 |
-define(EXIT,8115). | % |
退出系统 |
-define(SENDMSG,8116). | % |
发送消息 |
start() ->
Username = checkLoginInputInfo(usernameDialog()), % 获取登入用户名
Password = checkLoginInputInfo(passwordDialog()), % 获取用户密码
Socket = connect(), % 建立Tcp链接,获取Socket
ChatRoomList = loginServer(Username,Password,Socket), % 登入服务器,获取聊天房间列表
ChatRoom = checkLoginInputInfo(choseChatRoom(ChatRoomList)), % 获取用户选择的聊天房间
loginChatRoom(Username,ChatRoom,Socket). % 登入聊天房间
% 获取登入用户名
usernameDialog() ->
Prompt = “Please entry your username!”,
CaptionInfo = “Login chat system”,
wx:new(),
Dialog = wxTextEntryDialog:new(wx:null(),Prompt,[{caption,CaptionInfo}]),
case wxTextEntryDialog:showModal(Dialog) of
?wxID_OK ->
Str = wxTextEntryDialog:getValue(Dialog);
?wxID_CANCEL ->
Str = null
end, wxTextEntryDialog:destroy(Dialog), Str.
% 获取用户密码
passwordDialog() ->
Prompt = “Please entry the password”,
CaptionInfo = “Login chat system”,
wx:new(),
Dialog = wxPasswordEntryDialog:new(wx:null(),Prompt,[{caption,CaptionInfo}]),
case wxPasswordEntryDialog:showModal(Dialog) of
?wxID_OK ->
Str = wxPasswordEntryDialog:getValue(Dialog);
?wxID_CANCEL ->
Str = null
end, wxPasswordEntryDialog:destroy(Dialog), Str.
% 消息对话框,给 消息事件 调用
messageDialog(Prompt,CaptionInfo) ->
wx:new(),
Dialog = wxMessageDialog:new(wx:null(),Prompt,[{caption,CaptionInfo},{style,?wxO
K bor ?wxICON_INFORMATION}]),
wxMessageDialog:showModal(Dialog),
wxMessageDialog:destroy(Dialog).
% 检测 Str 是否为空 ,即:用户名,用户密码,所选聊天房间
checkLoginInputInfo(Str) ->
case Str of
null ->
wx:new(),
Prompt = “Thank you for use!”,
CaptionInfo = “Logout chat system”,
messageDialog(Prompt,CaptionInfo),
exit(self()); % 如果用户在某次选择了cancel,表明用户向退出聊天系统,即退出
_ -> Str
end.
% 与服务器建立链接,返回 Socket
connect() ->
case gen_tcp:connect(“localhost”,1110,[binary,{packet,0}]) of
{ok,Socket} ->
wx:new(),
Prompt = “Connect to the server success!”,
CaptionInfo = “Connect to the server …”,
messageDialog(Prompt,CaptionInfo),
Socket;
{error,Why} ->
CaptionInfo = “Connect to the server failed,is reconnecting”,
messageDialog(Why,CaptionInfo),
connect()
end.
% 登入服务器,登入成功获取聊天房间列表
loginServer(Username,Password,Socket) ->
gen_tcp:send(Socket,term_to_binary({login,Username,Password})),
receive
{tcp,Socket,Bin} ->
ChatRoomList = binary_to_term(Bin)
end,
case ChatRoomList of
{roomlist,[]} -> % 服务器返回聊天房间为空,表明 用户名/密码 有误
Prompt = “username or password error! please try again!”,
CaptionInfo = “Login chat system failed”,
messageDialog(Prompt,CaptionInfo),
start();
{roomlist,RoomList} -> RoomList
end.
% 选择聊天房间
choseChatRoom(ChatRoomList) ->
Prompt = “Please select the chat room!”,
CaptionInfo = “Chat room list”,
wx:new(),
Dialog = wxSingleChoiceDialog:new(wx:null(),Prompt,CaptionInfo,ChatRoomList),
case wxSingleChoiceDialog:showModal(Dialog) of
?wxID_OK ->
ChatRoom = wxSingleChoiceDialog:getStringSelection(Dialog);
?wxID_CANCEL ->
ChatRoom = null
end,
wxSingleChoiceDialog:destroy(Dialog),
ChatRoom.
% 登入聊天房间
loginChatRoom(Username,ChatRoom,Socket) ->
% 向服务器发送登入的聊天房间,服务收到该信息后,保存对该用户的链接的socket
gen_tcp:send(Socket,term_to_binary({socket,ChatRoom})),
CaptionInfo = “Online chat system — “++ChatRoom ++ ” — ” ++Username,
wx:new(),
% 聊天主框架
Frame = wxFrame:new(wx:null(),?wxID_ANY, CaptionInfo,[{pos,{300,300}},{size,{400,300}},
{style,?wxDEFAULT_FRAME_STYLE band (bnot(?wxRESIZE_BORDER bor ?wxMAXIMIZE BOX))}]),
% 聊天信息显示窗口
TextDisplay = wxTextCtrl:new(Frame,?wxID_ANY,
[{pos,{0,0}}, {size,{393,150}},{style,?wxTE_MULTILINE}]),
% 聊天信息输入窗口
TextInput = wxTextCtrl:new(Frame,?wxID_ANY,
[{pos,{0,150}}, {size,{393,80}},{style,?wxTE_MULTILINE}]),
setup(Frame,TextDisplay,TextInput),
wxFrame:show(Frame), loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket),
wx:destory().
% 绘制聊天窗口
setup(Frame,TextDisplay,TextInput) ->
MenuBar = wxMenuBar:new(),
Account = wxMenu:new(),
Statistics = wxMenu:new(),
Help = wxMenu:new(),
SendMessage = wxMenu:new(),
wxMenu:append(Help,?ABOUT,“About chat system”), wxMenu:append(Statistics,?ONLINEUSERNUM,“online users”), wxMenu:append(Account,?LOGINTIMES,“login times”), wxMenu:append(Account,?CHATTIMES,“chat times”), wxMenu:append(Account,?MYACCOUNT,“my account”), wxMenu:append(Account,?EXIT,“QUIT”), wxMenu:append(SendMessage,?SENDMSG,“Send Message”),
wxMenuBar:append(MenuBar,Account,“&Account”), wxMenuBar:append(MenuBar,Statistics,“&Statistics”), wxMenuBar:append(MenuBar,Help,“&Help”), wxMenuBar:append(MenuBar,SendMessage,“&SendMessage”),
wxFrame:setMenuBar(Frame,MenuBar),
wxTextCtrl:setEditable(TextDisplay,false),% 显示窗口不允许用户输入信息
wxTextCtrl:setEditable(TextInput,true),
wxFrame:createStatusBar(Frame), wxFrame:setStatusText(Frame,“Welcome to chat system!”),
wxFrame:connect(Frame,command_menu_selected), wxFrame:connect(Frame,close_window).
% 消息控制,监听窗口消息以及服务器发送过来的消息
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket) ->
receive
% 退出聊天系统
#wx{id = ?EXIT, event = #wxCommand{type=command_menu_selected}} ->
wxWindow:close(Frame,[]);
% 查看帮助信息
#wx{id = ?ABOUT,event = #wxCommand{type=command_menu_selected}} ->
wx:new(),
Prompt = “Chat online system”,
CaptionInfo = “About”,
messageDialog(Prompt,CaptionInfo),
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket);
% 查看在线用户数量
#wx{id = ?ONLINEUSERNUM,event = #wxCommand{type = command_menu_selected}} ->
gen_tcp:send(Socket,term_to_binary({totalusers,ChatRoom})),
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket);
% 查看登陆次数
#wx{id = ?LOGINTIMES,event = #wxCommand{type = command_menu_selected}} ->
gen_tcp:send(Socket,term_to_binary({logintimes,Username})),
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket);
% 查看聊天次数
#wx{id = ?CHATTIMES,event = #wxCommand{type = command_menu_selected}} ->
gen_tcp:send(Socket,term_to_binary({chattimes,Username})),
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket);
% 查看我上一次登陆时间
#wx{id = ?MYACCOUNT,event = #wxCommand{type = command_menu_selected}} ->
gen_tcp:send(Socket,term_to_binary({lastlogin,Username})), loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket);
% 发送聊天消息
#wx{id = ?SENDMSG,event = #wxCommand{type = command_menu_selected}} ->
Msg = wxTextCtrl:getValue(TextInput), % 获取输入消息
gen_tcp:send(Socket,term_to_binary({msg,ChatRoom,Username,Msg})),
wxTextCtrl:clear(TextInput), % 清空输入框
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket);
% 收取来自服务器的消息
{tcp,Socket,Bin} ->
case binary_to_term(Bin) of
% 聊天消息
{msg,User_name,Msg} ->
wxTextCtrl:writeText(TextDisplay,User_name++” >> “++Msg++“r“);
% 登陆次数
{logintimes,LoginTimes} ->
wx:new(),
Prompt = lists:concat(["Your total login times is ",LoginTimes]),
CaptionInfo = “Login Times”, messageDialog(Prompt,CaptionInfo);
% 聊天次数
{chattimes,ChatTimes} ->
wx:new(),
Prompt = lists:concat(["Your total chat times is ",ChatTimes]),
CaptionInfo = “Chat Times”, messageDialog(Prompt,CaptionInfo);
% 最近一次登陆时间
{lastlogin,LastTime} ->
wx:new(),
Prompt = io_lib:format(“Your last login times is ~p“,[LastTime]),
CaptionInfo = “Last login Time”, messageDialog(Prompt,CaptionInfo);
% 在线用户数目
{totalusers,TotalUserNum} ->
wx:new(),
Prompt = lists:concat(["Current online user number is",TotalUserNum]),
CaptionInfo = “Online user num”,
messageDialog(Prompt,CaptionInfo)
end,
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket)
end.
程序运行:
输入用户名
输入密码
聊天展示
请求在线人数
目前聊天室Education在线人数 3人
请求 登入次数
用户 hua 登入过 4次
请求 聊天次数
用户 hua 的聊天次数共 6次
请求 最近一次登入系统的时间
用户 hua 最近一次登入系统时间 2011:7:13 14:49:28
用户 退出聊天室
数据库设计,共三个表
表:account
用户名 | 用户密码 | 用户昵称 | 登入次数 | 聊天次数 | 最近登入时间 |
表 chatroom
表 roomsocket