转载:第一个Erlang 小程序

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 转自:http://ovalpo.info/?p=362 学了Erlang三个礼拜,做了这个程序,费了4天时间,这四天查阅了wxwidgts,wxErlang,等界面设计的资料,英文还是不行 呀!今天被老大说我的程序使用的协议不规范。

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


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Python
Python的第一个程序
【4月更文挑战第8天】Python的第一个程序
35 6
|
9月前
|
Python
python-- 启动进程的两种方式
python-- 启动进程的两种方式
|
10月前
|
Python
02-Python-编写第一个程序
02-Python-编写第一个程序
|
程序员 开发者 Python
for循环在Python中是怎么工作的
for循环在Python中是怎么工作的
32 0
|
Python
解决办法:C向Python传递字串数组导致进程崩溃
解决办法:C向Python传递字串数组导致进程崩溃
120 0
|
存储 程序员 Linux
我的第一个Python程序
Python 环境安装好了,可以进行Python程序编写了,但在哪里编写Python程序呢?是在 cmd 窗口中输入Python 打开的Python交互器编写吗?还是在记事本中?刚入门都可以,但还是建议先用记事本来编写。到后面有专门的Python开发工具 PyCharm。
uiu
|
存储 前端开发 索引
我的Go+语言初体验——GO+实现数据结构之【队列与循环列表】(3)
我的Go+语言初体验——GO+实现数据结构之【队列与循环列表】(3)
uiu
89 0
我的Go+语言初体验——GO+实现数据结构之【队列与循环列表】(3)
|
Shell Python Windows
python的第一个程序
如果是用windows,请打开CMD,并执行python。 如果是UNIX类的,就运行shell,并执行python。 都会出现如下内容: Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34) [GCC 7.3.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> print "hello world" hello world >>> 在>>>后面输入下面内容,并按回车。
864 0
|
Python 开发工具 IDE
python:LEGB标识符解析顺序
LEGB标识符解析顺序
1296 0