Erlang实现简单聊天室
在本教程中,我们将使用Erlang语言实现一个简单的聊天室。该聊天室由一个服务器和多个客户端组成,服务器负责监听客户端的连接,并将客户端发送的消息群发给所有已连接的客户端。
1. chat_server.erl
此模块实现了聊天室的服务器功能,包括监听客户端连接、管理客户端列表以及广播消息。
-module(chat_server).
-export([start/1]).
-define(TCP_OPTIONS, [list, {packet, 0}, {active, false}, {reuseaddr, true}]).
%% 启动服务器
start(Port) ->
Pid = spawn(fun() -> manage_clients([]) end),
register(client_manager, Pid),
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
do_accept(LSocket).
%% 接受客户端连接
do_accept(LSocket) ->
{ok, Socket} = gen_tcp:accept(LSocket),
spawn(fun() -> handle_client(Socket) end),
client_manager ! {connect, Socket},
do_accept(LSocket).
%% 处理客户端消息
handle_client(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
client_manager ! {data, Data},
handle_client(Socket);
{error, closed} ->
client_manager ! {disconnect, Socket}
end.
%% 管理客户端列表
manage_clients(Sockets) ->
receive
{connect, Socket} ->
io:fwrite("Socket connected: ~w~n", [Socket]),
NewSockets = [Socket | Sockets];
{disconnect, Socket} ->
io:fwrite("Socket disconnected: ~w~n", [Socket]),
NewSockets = lists:delete(Socket, Sockets);
{data, Data} ->
send_data(Sockets, Data),
NewSockets = Sockets
end,
manage_clients(NewSockets).
%% 向所有客户端发送消息
send_data(Sockets, Data) ->
SendData = fun(Socket) ->
gen_tcp:send(Socket, Data)
end,
lists:foreach(SendData, Sockets).
代码解释
start/1:启动服务器,创建一个进程来管理客户端列表,并监听指定端口的连接。do_accept/1:接受新的客户端连接,为每个客户端创建一个新的进程来处理其消息,并通知客户端管理器有新的客户端连接。handle_client/1:处理客户端发送的消息,将消息转发给客户端管理器,并继续等待客户端的下一条消息。manage_clients/1:管理客户端列表,处理客户端的连接、断开连接和消息广播。send_data/2:将消息发送给所有已连接的客户端。
2. chat_send_client.erl
此模块实现了一个客户端,用于向服务器发送消息。
-module(chat_send_client).
-export([start/2]).
%% 启动发送客户端
start(IP, Port) ->
{ok, Socket} = gen_tcp:connect(IP, Port, [binary, {packet, 4}]),
talk(Socket).
%% 发送消息
talk(Socket) ->
Msg = io:get_line('Input you msg:'),
ok = gen_tcp:send(Socket, term_to_binary(Msg)),
talk(Socket).
代码解释
start/2:连接到服务器,并启动一个循环来不断读取用户输入的消息并发送给服务器。talk/1:读取用户输入的消息,将其转换为二进制数据并发送给服务器,然后继续等待用户的下一条消息。
3. chat_recv_client.erl
此模块实现了一个客户端,用于接收服务器广播的消息。
-module(chat_recv_client).
-export([start/2]).
%% 启动接收客户端
start(IP, Port) ->
{ok, Socket} = gen_tcp:connect(IP, Port, [binary, {packet, 4}]),
recv_msg(Socket).
%% 接收消息
recv_msg(Socket) ->
receive
{tcp, Socket, Bin} ->
Msg = binary_to_term(Bin),
io:format("Received msg: ~p~n", [Msg]),
recv_msg(Socket)
end.
代码解释
start/2:连接到服务器,并启动一个循环来不断接收服务器广播的消息。recv_msg/1:接收服务器发送的二进制数据,将其转换为原始消息并打印出来,然后继续等待服务器的下一条消息。
4. 启动服务器
在Erlang shell中执行以下命令启动服务器:
Eshell> chat_server:start(9999).
5. 启动发送和接收客户端进程
在不同的Erlang shell中分别启动发送和接收客户端进程:
%% client1
Eshell> chat_send_client:start("localhost", 9999).
Eshell> chat_recv_client:start("localhost", 9999).
%% client2
Eshell> chat_send_client:start("localhost", 9999).
Eshell> chat_recv_client:start("localhost", 9999).
6. 交互过程
启动客户端后,你可以在发送客户端输入消息,然后在接收客户端看到这些消息。例如:
%% client1/send
Input you msg:我囧
%% client2/send
Input you msg:你囧啥?
%% client1、2/recv
Received msg: "我囧\n"
Received msg: "你囧啥?\n"
通过以上步骤,你就可以使用Erlang实现一个简单的聊天室。这个聊天室可以让多个客户端连接到服务器,并实时发送和接收消息。