erlang轻松实现memcached binary协议
简单实现了下memcached binary protocol的 get和 set command,体验了下erlang binary语法的强大和方便
代码:
- -module(binary_server).
- -export([start/0]).
- start() ->
- {ok,Listen}=gen_tcp:listen(7777,[binary,{packet,0},{reuseaddr,true},{active,true}]),
- register(kvs,spawn(fun() -> dict() end)),
- accept(Listen).
- accept(Listen) ->
- {ok,Socket}=gen_tcp:accept(Listen),
- spawn(fun() -> accept(Listen) end),
- inet:setopts(Socket,[binary,{packet,0},{active,true}]),
- process(Socket,<<>>).
- process(Socket,Left) ->
- receive
- {tcp,Socket,Bin} ->
- Buffer=list_to_binary(binary_to_list(Left) ++ binary_to_list(Bin)),
- case regonized(Buffer) of
- {save_ok,RealRemaning} ->
- Resp=[16#81,16#1] ++ fill_all(0,22,[]),
- gen_tcp:send(Socket,list_to_binary(Resp)),
- process(Socket,RealRemaning);
- {get_ok,undefined,Remaining} ->
- Value= <<"not_found">>,
- BodyLen=length(binary_to_list(Value))+4,
- Resp=[16#81] ++ fill_all(0,3,[]) ++[16#4]
- ++ fill_all(0,3,[]) ++
- binary_to_list(<<BodyLen:32>>) ++
- binary_to_list(<<0:128>>) ++
- binary_to_list(Value),
- gen_tcp:send(Socket,list_to_binary(Resp)),
- process(Socket,Remaining);
- {get_ok,{ok,Value},Remaining} ->
- BodyLen=length(binary_to_list(Value))+4,
- Resp=[16#81] ++ fill_all(0,3,[]) ++[16#4]
- ++ fill_all(0,3,[]) ++
- binary_to_list(<<BodyLen:32>>) ++
- binary_to_list(<<0:128>>) ++
- binary_to_list(Value),
- gen_tcp:send(Socket,list_to_binary(Resp)),
- process(Socket,Remaining);
- {get_timeout,Remaining} ->
- process(Socket,Remaining);
- {not_engouh_streams} ->
- process(Socket,Buffer)
- end;
- {tcp_closed,Socket} ->
- io:format("peer closed~n")
- end.
- regonized(Buffer) ->
- case Buffer of
- %%set command
- <<16#80:8,16#1:8,KeyLen:16/big,
- ExtraLen:8/big,DataType:8,Reserved:16/big,
- BodyLen:32/big,Opaque:32/big,Cas:64/big,
- Extras:64/big,Key:KeyLen/big-binary-unit:8,
- Remaning/binary>> ->
- ValueLen=BodyLen-KeyLen-ExtraLen,
- case Remaning of
- <<Value:ValueLen/binary-unit:8,
- RealRemaning/binary>> ->
- %% got completed packet,deal with set command
- kvs ! {self(),{store,Key,Value}},
- {save_ok,RealRemaning};
- <<_/binary>> ->
- %% not enough streams
- {not_enough_streams}
- end;
- %%get command
- <<16#80:8,16#0:8,KeyLen:16/big,16#0:8,
- DataType:8,Reserved:16/big,BodyLen:32/big,
- Opaque:32/big,Cas:64/big,Key:KeyLen/big-binary-unit:8,
- Remaining/binary>> ->
- kvs ! {self(),{lookup,Key}},
- receive
- {lookup,Value} ->
- {get_ok,Value,Remaining}
- after 1000 ->
- {get_timeout,Remaining}
- end;
- %% no match
- <<_/binary>> ->
- {not_engouh_streams}
- end.
- dict() ->
- receive
- {From,{store,Key,Value}} ->
- put(Key,{ok,Value}),
- dict();
- {From,{lookup,Key}} ->
- From ! {lookup,get(Key)},
- dict()
- end.
- debug_print([H|T]) ->
- io:format("~w,",[H]),
- debug_print(T);
- debug_print([])->
- ok.
- fill_all(C,0,L)->
- L;
- fill_all(C,N,L)->
- N1=N-1,
- L1=[C|L],
- fill_all(C,N1,L1).
说明:
1. 16#80:8
16#代表16进制,80是memcached协议头的第一个字节 80代表request
具体协议内容memcached官网上有很详尽的解释
2. KeyLen:16/big
协议中key的长度是占2个字节 ,16是bit数 .
因为多于一个字节的数据在存储和传输时就会涉及字节序问题,
这里big代表的是大端/网络字节序,
因为我的测试client在传输数据前已经将数据转成网络字节序,所以这里接收必须是big
3. Key:KeyLen/big-binary-unit:8 ,
这里Key具体的字节数是由之前得到的KeyLen指定的,所以表示为Key:KeyLen,
因为这里的单位应该是字节,所以需要指定为unit:8,其实这里指定了binary
binary类型默认的unit就是8,即 实际的size = KeyLen * unit 个 bit
4. lookup没有找到指定key对应value时,这里没有按协议处理,以not_found为value返回给client,实际协议是返回一个status为非0的协议包
原文地址:http://www.iteye.com/topic/401737
上善若水
浙公网安备 33010602011771号