菜鸟一枚,老大说要做个socket聊天室,用socket实现,所以就网上一搜基本都没什么资料,这里贡献一篇我自己写的很简陋的多人聊天室,有不对的地方,多多指出,谢谢。
- 基本要求:
1. 写一个支持多人在线的socket聊天服务器和客户端,需要实现以下功能:
a. 用户登录,用户管理相关的功能无需实现,手工构造几个用户数据就可以了;
b. 实现用户登录,聊天和退出功能。用户数据中需要记录登录次数和聊天次数;
c. 能够统计当前在线用户数;
d. 用户数据存储在ets中;
e. 定义服务端和客户的端通讯协议;
%% 用户数据结构:
-record(user, {
id %% 用户ID
,name %% 用户名称
,passwd %% 用户登录密码
,login_times %% 登录次数
,chat_times %% 聊天次数
,last_login %% 最后一次登录时间
,state %% 登录状态 0 未登录 1已登录
}
).
服务端代码:
-module(server_example).
%-export([start/0,initialize_ets/0,info_lookup/1,loop/2,info_update/3,init/1,handle_call/3,terminate/2]).
%-import(counter,[start/1,add/1,value/1,decrease/1,log_add/1,chat_add/1]).
%-import(my_fsm,[start_count/0,count/1]).
-compile(export_all).
-include("user_info.hrl").
-define(SERVER,?MODULE).
start() ->
gen_server:start_link({local,?SERVER},?MODULE,[],[]).
init([]) ->
initialize_ets(),
start_parallel_server(),
{ok,ets:new(mysocket,[public,named_table])}.
%开启服务器
start_parallel_server() ->
{ok,Listen} = gen_tcp:listen(2345,[binary,{packet,0},{reuseaddr,true},{active,true}]),
spawn(fun() -> per_connect(Listen) end).
%每次绑定一个当前Socket后再分裂一个新的服务端进程,再接收新的请求
per_connect(Listen) ->
{ok,Socket} = gen_tcp:accept(Listen),
spawn(fun() -> per_connect(Listen) end),
loop(Socket).
%初始化ets
initialize_ets() ->
ets:new(test,[set,public,named_table,{keypos,#user.name}]),
ets:insert(test,#user{id=01,name="carlos",passwd="123",login_times=0,chat_times=0,last_login={},state=0}),
ets:insert(test,#user{id=02,name="qiqi",passwd="123",login_times=0,chat_times=0,last_login={},state=0}),
ets:insert(test,#user{id=03,name="cym",passwd="123",login_times=0,chat_times=0,last_login={},state=0}).
%查询ets
info_lookup(Key) ->
%返回值是一个元组
ets:lookup(test,Key).
%修改ets信息
info_update(Key,Pos,Update) ->
ets:update_element(test,Key,{Pos,Update}).
%<<-------------------------回调函数----------------------------->>
%登录的时候添加socket
handle_call({addSocket,UserName,Socket},_From,Tab) ->
Reply = case ets:lookup(Tab,UserName) of
[{UserName,Socket}] -> have_socket;
[] -> ets:insert(Tab,{UserName,Socket})
end, %这里是顺序结构,所以用逗号
io:format("Tab ~p~n",[Tab]),
% io:format("mysocket ~p~n",[ets:i(Tab)]),
{reply,Reply,Tab};
%退出的时候,删除socket
handle_call({deleteSocket,UserName,Socket},_From,Tab) ->
Reply = case ets:lookup(Tab,UserName) of
[{UserName,Socket}] -> ets:delete(Tab,UserName);
[] -> io:format("no exist this socket ~p~n",[Socket])
end, %这里是顺序结构,所以用逗号
% io:format("mysocket ~p~n",[ets:i(Tab)]),
{reply,Reply,Tab};
%用户在线个数
handle_call({userNumber},_From,Tab) ->
Socketlist = ets:tab2list(Tab),
io:format("~p user online~n",[length(Socketlist)]),
{reply,[],Tab};
%广播信息
handle_call({sendAllMessage,Name,Msg},_From,Tab) ->
%Socketlist = [{UserName,Socket,Name,Msg}||{UserName,Socket} <- ets:tab2list(Tab)], 不用推导了
Socketlist =[{UserName,Socket}||{UserName,Socket} <- ets:tab2list(Tab),UserName =/= Name],
io:format("list ~p~n",[Socketlist]),
lists:foreach(
fun({UserNa