天天看點

轉載:第一個Erlang 小程式

轉自: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