轉自:http://ovalpo.info/?p=362
學了Erlang三個禮拜,做了這個程式,費了4天時間,這四天查閱了wxwidgts,wxErlang,等界面設計的資料,英文還是不行 呀!今天被老大說我的程式使用的協定不規範。我想這算個RC版吧,哈哈。本程式使用Erlang + wxErlang + mysql。Erlang這東西不火,也是個麻煩,Web上Erlang的資料可憐地少,先貼個效果圖吧。
這個程式:不小心搞了個變量去比對消息,然後變量不變這個害我好慘。記住:Erlang變量特性,變量不變
程式很拙劣,但我還是想貼出來,以示我Erlang入門了,哈哈。
程式:線上多人聊天系統
伺服器端代碼:
-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);
io:format(“create internet connect failed,the reasom maybe ~p~n
now is going reconnect~n“,[Why]),
par_connect(Listen)
% 監聽消息
loop(Socket) ->
receive
{tcp,Socket,Bin} ->
case binary_to_term(Bin) of
{login,Username,Password} -> % 請求登入聊天系統
handleLoginRequest(Username,Password,Socket),
{socket,ChatRoom} -> % 使用者選擇了聊天房間
handleChoseRoom(ChatRoom,Socket),
{msg,ChatRoom,Username,Msg} -> % 使用者發送聊天資訊
handleChatMsg(ChatRoom,Username,Msg),
{totalusers,ChatRoom} -> % 使用者請求線上總人數
handleTotalUsersRequest(ChatRoom,Socket),
{logintimes,Username} -> % 使用者請求登入系統次數
handleLoginTimesRequest(Username,Socket),
{chattimes,Username} -> % 使用者請求聊天次數
handleChatTimesRequest(Username,Socket),
{lastlogin,Username} -> % 使用者請求最近一次登陸系統時間
handleLastLoginRequest(Username,Socket),
loop(Socket)
end;
{tcp_closed,Socket} -> % 使用者登出聊天系統
handleTcpClosed(Socket),
% 請求登入聊天系統
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
Sql3 = lists:concat(["update account set last_login = now()
where username = '",Username,"'"]), mysql:fetch(chat,Sql3) % 更新最近一次登入時間
% 檢查使用者名,密碼是否正确
checkAccount(Username,Password) ->
Sql = lists:concat(["select * from account where username = '",
Username,"'and password = '",Password,"'"]),
case mysql:fetch(chat,Sql) of
case mysql:get_result_rows(Result) of
[] -> nok; % 使用者名,密碼有誤
_ -> ok % 使用者名,密碼有效
{error,_Result} ->
checkAccount(Username,Password)
% 擷取聊天房間清單
getChatRoomList() ->
Sql = “select roomname from chatroom” ,
[] -> [];
RoomList -> binaryToList(RoomList) % 處理提取出來的二進制資料
getChatRoomList()
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,"'"] ),
[[Chattimes]] = mysql:get_result_rows(Result),
Sql2 = lists:concat(["update account set chat_times = ",Chattimes+1,
" where username = '",Username,"'"]),
mysql:fetch(chat,Sql2);
% 在聊天房間廣播使用者Username 的 Msg
sendRoomMsg(ChatRoom,Username,Msg).
% 向聊天房間廣播使用者 Username 的消息 Msg sendRoomMsg(ChatRoom,Username,Msg) ->
["select socket from roomsocket where roomname = '",ChatRoom,"'"] ),
[] -> nosocket;
SocketList ->
Sockets = binaryToList(SocketList), % 處理提取出來的二進制資料
sendMessage(Username,Sockets,Msg)
sendRoomMsg(ChatRoom,Username,Msg)
% 向房間内的所有使用者(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) ->
["select count(socket) from roomsocket where roomname = '",ChatRoom,"'"]),
[[Num]] = mysql:get_result_rows(Result),
gen_tcp:send(Socket,term_to_binary({totalusers,Num}));
% 使用者請求登入系統次數
handleLoginTimesRequest(Username,Socket) ->
["select login_times from account where username = '",Username,"'"]),
[[LoginTimes]] = mysql:get_result_rows(Result),
gen_tcp:send(Socket,term_to_binary({logintimes,LoginTimes}));
% 使用者請求聊天次數
handleChatTimesRequest(Username,Socket) ->
["select chat_times from account where username = '",Username,"'"]),
[[ChatTimes]] = mysql:get_result_rows(Result),
gen_tcp:send(Socket,term_to_binary({chattimes,ChatTimes}));
{error,_Result} ->no
% 使用者請求最近一次登陸系統時間
handleLastLoginRequest(Username,Socket) ->
["select last_login from account where username = '",Username,"'"]),
[[{ 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}));
% 使用者退出了聊天系統,删除使用者的在資料庫中的 socket記錄
handleTcpClosed(Socket) ->
Sql = lists:concat(["delete from roomsocket where socket = '",Soc,"'"]),
用戶端代碼:
-module(client).
-include_lib(“wx/include/wx.hrl”).
-define(ABOUT,8110).
%
關于系統
-define(ONLINEUSERNUM,8111).
線上使用者數(同一個聊天房間)
-define(LOGINTIMES,8112).
使用者登入次數
-define(CHATTIMES,8113).
使用者聊天次數(沒法送一條 msg,聊天次數 +1)
-define(MYACCOUNT,8114).
使用者最後一次登入時間
-define(EXIT,8115).
退出系統
-define(SENDMSG,8116).
發送消息
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”,
Dialog = wxPasswordEntryDialog:new(wx:null(),Prompt,[{caption,CaptionInfo}]),
case wxPasswordEntryDialog:showModal(Dialog) of
Str = wxPasswordEntryDialog:getValue(Dialog);
end, wxPasswordEntryDialog:destroy(Dialog), Str.
% 消息對話框,給 消息事件 調用
messageDialog(Prompt,CaptionInfo) ->
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 ->
Prompt = “Thank you for use!”,
CaptionInfo = “Logout chat system”,
messageDialog(Prompt,CaptionInfo),
exit(self()); % 如果使用者在某次選擇了cancel,表明使用者向退出聊天系統,即退出
_ -> Str
% 與伺服器建立連結,傳回 Socket
connect() ->
case gen_tcp:connect(“localhost”,1110,[binary,{packet,0}]) of
Prompt = “Connect to the server success!”,
CaptionInfo = “Connect to the server …”,
Socket;
CaptionInfo = “Connect to the server failed,is reconnecting”,
messageDialog(Why,CaptionInfo),
connect()
% 登入伺服器,登入成功擷取聊天房間清單
loginServer(Username,Password,Socket) ->
gen_tcp:send(Socket,term_to_binary({login,Username,Password})),
ChatRoomList = binary_to_term(Bin)
case ChatRoomList of
{roomlist,[]} -> % 伺服器傳回聊天房間為空,表明 使用者名/密碼 有誤
Prompt = “username or password error! please try again!”,
CaptionInfo = “Login chat system failed”,
start();
{roomlist,RoomList} -> RoomList
% 選擇聊天房間
choseChatRoom(ChatRoomList) ->
Prompt = “Please select the chat room!”,
CaptionInfo = “Chat room list”,
Dialog = wxSingleChoiceDialog:new(wx:null(),Prompt,CaptionInfo,ChatRoomList),
case wxSingleChoiceDialog:showModal(Dialog) of
ChatRoom = wxSingleChoiceDialog:getStringSelection(Dialog);
ChatRoom = null
wxSingleChoiceDialog:destroy(Dialog),
ChatRoom.
% 登入聊天房間
loginChatRoom(Username,ChatRoom,Socket) ->
% 向伺服器發送登入的聊天房間,服務收到該資訊後,儲存對該使用者的連結的socket
gen_tcp:send(Socket,term_to_binary({socket,ChatRoom})),
CaptionInfo = “Online chat system — “++ChatRoom ++ ” — ” ++Username,
% 聊天主架構
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) ->
% 退出聊天系統
#wx{id = ?EXIT, event = #wxCommand{type=command_menu_selected}} ->
wxWindow:close(Frame,[]);
% 檢視幫助資訊
#wx{id = ?ABOUT,event = #wxCommand{type=command_menu_selected}} ->
Prompt = “Chat online system”,
CaptionInfo = “About”,
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})),
% 檢視登陸次數
#wx{id = ?LOGINTIMES,event = #wxCommand{type = command_menu_selected}} ->
gen_tcp:send(Socket,term_to_binary({logintimes,Username})),
% 檢視聊天次數
#wx{id = ?CHATTIMES,event = #wxCommand{type = command_menu_selected}} ->
gen_tcp:send(Socket,term_to_binary({chattimes,Username})),
% 檢視我上一次登陸時間
#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), % 清空輸入框
% 收取來自伺服器的消息
% 聊天消息
{msg,User_name,Msg} ->
wxTextCtrl:writeText(TextDisplay,User_name++” >> “++Msg++“r“);
% 登陸次數
{logintimes,LoginTimes} ->
Prompt = lists:concat(["Your total login times is ",LoginTimes]),
CaptionInfo = “Login Times”, messageDialog(Prompt,CaptionInfo);
% 聊天次數
{chattimes,ChatTimes} ->
Prompt = lists:concat(["Your total chat times is ",ChatTimes]),
CaptionInfo = “Chat Times”, messageDialog(Prompt,CaptionInfo);
% 最近一次登陸時間
{lastlogin,LastTime} ->
Prompt = io_lib:format(“Your last login times is ~p“,[LastTime]),
CaptionInfo = “Last login Time”, messageDialog(Prompt,CaptionInfo);
% 線上使用者數目
{totalusers,TotalUserNum} ->
Prompt = lists:concat(["Current online user number is",TotalUserNum]),
CaptionInfo = “Online user num”,
messageDialog(Prompt,CaptionInfo)
loop(Frame,TextDisplay,TextInput,ChatRoom,Username,Socket)
程式運作:
輸入使用者名
輸入密碼
聊天展示
請求線上人數
目前聊天室Education線上人數 3人
請求 登入次數
使用者 hua 登入過 4次
請求 聊天次數
使用者 hua 的聊天次數共 6次
請求 最近一次登入系統的時間
使用者 hua 最近一次登入系統時間 2011:7:13 14:49:28
使用者 退出聊天室
資料庫設計,共三個表
表:account
使用者名
使用者密碼
使用者昵稱
登入次數
聊天次數
最近登入時間
表 chatroom
表 roomsocket