erlang解決socketデータの接着問題

6977 ワード

我々は、erlangが実現するネットワークサーバの性能が非常に高いことを知っています。erlangの効率は短い数行のコードではなく、多くのコードを使わず、効率的なサービス端末プログラムを書くことができます。このすべての背後にあるのは、erlangが多くのネットワーク操作に対してほぼ完璧なパッケージを実現し、その恩恵を受けています。文章はerlang gen_を討論します。tcpデータ接続問題とerlangの解決策。
データパッケージの問題は、これはclient/serverの通信でよく見られます。つまり、clientが極短期間に複数のパッケージをserverに送信すると、serverはデータを受信する時にパケット問題が発生し、このいくつかのパケットのデータを一度に受信すると、データが全部くっついてしまいます。
ここでまず討論します。 raw}または{packet、0}の場合、それぞれ{active、Boolean}の二つの方式を見ます。
gen_tcpによるsocketパケットの取得には、以下の2つの方法があります。
1、{active、false}方式はgen_を通じてtcp:recv(Socket,Length) -> {ok,Data}{error,Reason} を選択します2、{active、true}メッセージ形式で{tcp、Socket、Data} {tcpuclosed,Socket}自発的にスレッドに投稿します。
第一の方式に対してはgen_tcp:recv/2,3の場合、封緘のタイプが{packet} raw}または{packet,0}は明示的な指定長さが必要です。そうでないと、パッケージの長さは対端で決まります。長さは0にしか設定できません。長さLengthが0に設定されている場合、gen_tcp:recv/2,3はソケット受信バッファのすべてのデータを取り出します。
第二の方法では、キャッシュエリアにはどのぐらいのデータがありますか?全部メッセージです。 スレッドに送る
以上のようにデータパッケージの問題を引き起こしますが、どう解決しますか?
 {packet、PacketType}
今から見に来てください。erlangの説明は以下の通りです。
「packet,PacketType」(TCP/IP sockets)Defines the type of packets to use for a socket.The follwing values arvalid:raw|0 No packaging is done.1|2 Packets 4 Packets consist.Thaheadadininininininininininaaaaadedededededededededededededededededededededededededededededededededededededededepapapapapapapapapapapapapapapapapapapapapapapapapapapapapapapapapapapapackaaaaaaaaaaone、two、or four bytes;containinininininininingned integer in big-endian byte order.Each send operation will generaae the header、andthe header will streipped oooorereceive operati. Incurrentimplemenatithe 4-byte headadinininininininininininininininininininininininininininininininininininininininininininininininine the the the 4-aaaaaaaathe 4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaate the、and types only have effect on receiving.When sending a packet、it is the reponsibility of the apply to supply a corect header.On receiving,however,there will be one message sent to the controloging process for each complette packet received,and,simillary,eal to gantcp:recv/2,3 returns one complete packet.The header is not streipped off.The meanings of the packet types are as follows:  asn 1-ASN.1 BER、  sunrm-Sun's RPC encoding,  cdr-CORBA(GIOP 1.1)、  fcgi-Fast CGI  tpkt-TPKT format[RFC 1006]  line-Line mode,a packet is a line terminated with newline,line longer than the receive buffer are truncated.http|httpBin The Hypertext Transfer Protocol.The packets are returned with the format accoding to HttpPacket described in erlang:decode_packet/3.A socket in passive mode will return{ok,HttpPacket}from gen_tcp:recv while an active socket will send messages like{http,Socket,HttpPacket}Bin These two types are offten not needed as the socket will autically switch from http/http_bin to httph/httph_bin internally after the first line has been read.The e e might be occasions however when they the useful,such as parsing trilers from chunked encoding.
packetの大体の意味は以下の通りです。
raw_124 0は、パケットがない、つまり、パケットヘッドに関係なく、Lengthパラメータに従ってデータを受信する。1|2|4は、パケットの長さを表し、それぞれ1,2,4バイト(2,4は大きなバイト順、符号なしで表される)である。このパラメータを設定すると、データを受信すると、対応する長さのヘッダが自動的に剥離され、Bodyのみが保持される。asn 1|cdr𞓜sunrm fcgi𞓜tpkt 124;ラインに上記のパラメータを設定すると、アプリケーションはパケットヘッドの正確性を保証しますが、gen_tcp:recv/2,3受信したパケットのうち、頭部を剥離していません。http𞓜http_以上のパラメータを設定すると、受信したデータはerlang:decode_になります。パッケート/3フォーマットで、パッシブモードで{ok,HttpPacket}を受け取ります。アクティブモードでは{http,Socket,HttpPacket}を受け取ります。  
 {packet} N}
つまり、packet属性が1,2,4であれば、server端が一回受信するパケットサイズを保証することができる。
次に私たちは{packet,2}で討論します。
gen_tcp通信伝送のデータは、パケット+データの2つの部分を含む。gen_tcp:send/2データを送信すると、erlangは送信データのサイズを計算して、サイズ情報をヘッダに預けて、封包をして送信します。
したがって、データを受信するときは、ヘッダ情報に基づいて、受信データのサイズを判断します。gen_を使うtcp:recv/2,3データを受信すると、erlangはパケットヘッダを自動的に処理して、パケットデータを取得します。
以下に例を書いて説明します。 tcp_test.erl
-module(tcp_test).
-export([
    start_server/0,
    start_client_unpack/0, start_client_packed/0
    ]).

-define(PORT, 8888).
-define(PORT2, 8889).

start_server()->
	{ok, ListenSocket} = gen_tcp:listen(?PORT, [binary,{active,false}]),
	{ok, ListenSocket2} = gen_tcp:listen(?PORT2, [binary,{active,false},{packet,2}]),
	spawn(fun() -> accept(ListenSocket) end),
	spawn(fun() -> accept(ListenSocket2) end),
	receive
		_ -> ok
	end.

accept(ListenSocket)->
	case gen_tcp:accept(ListenSocket) of
		{ok, Socket} ->
			spawn(fun() -> accept(ListenSocket) end),
			loop(Socket);
		_ ->
			ok
	end.

loop(Socket)->
	case gen_tcp:recv(Socket,0) of
		{ok, Data}->
			io:format("received message ~p~n", [Data]),
			gen_tcp:send(Socket, "receive successful"),
			loop(Socket);
		{error, Reason}->
			io:format("socket error: ~p~n", [Reason])
	end.

start_client_unpack()->
	{ok,Socket} = gen_tcp:connect({127,0,0,1},?PORT,[binary,{active,false}]),
	gen_tcp:send(Socket, "1"),
	gen_tcp:send(Socket, "2"),
	gen_tcp:send(Socket, "3"),
	gen_tcp:send(Socket, "4"),
	gen_tcp:send(Socket, "5"),
	sleep(1000).

start_client_packed()->
	{ok,Socket} = gen_tcp:connect({127,0,0,1},?PORT2,[binary,{active,false},{packet,2}]),
	gen_tcp:send(Socket, "1"),
	gen_tcp:send(Socket, "2"),
	gen_tcp:send(Socket, "3"),
	gen_tcp:send(Socket, "4"),
	gen_tcp:send(Socket, "5"),
	sleep(1000).

sleep(Count) ->
	receive
	after Count ->
		ok
	end.
は以下のように動作します。
C:\>erlc tcp_test.erl
C:\>erl -s tcp_test start_server
Eshell V5.10.2  (abort with ^G)

1> tcp_test:start_client_packed().
received message <<"1">>
received message <<"2">>
received message <<"3">>
received message <<"4">>
received message <<"5">>
ok

2> tcp_test:start_client_unpack().
received message <<"12345">>
ok
バイト順
バイト順は2種類に分けられています。Big-EndanとLittle-Endanは下記のように定義されています。b)Big-Endanとは、高バイトがメモリの低アドレス端に排出され、低ビットバイトがメモリの高アドレス端に排出されることである。実はもう一つのネットバイトの順序があります。TCP/IP各層のプロトコルに定義されているバイトの順序はBig-Endanです。
packetヘッダは、大きなバイト順で表される。もしerlangと他の言語、例えばC+++、バイト順に注意します。マシンのバイト順がリトルエンディアンなら、変換します。
{packet,2}:[L 1,L 0|Data]
{packet,4}:[L 3,L 2,L 1,L 0|Data]
マシンのバイト順はどう判断しますか?C++を例にします。
BOOL IsBigEndian()  
{  
    int a = 0x1234;  
    char b =  *(char *)&a;  //   int       char   ,            
    if( b == 0x12)  
    {  
        return TRUE;  
    }  
    return FALSE;  
}
バイト順は、C++を例にしてどのように変換されますか?
// 32    
#define LittletoBig32(A)   ((( (UINT)(A) & 0xff000000) >> 24) | \
	(( (UINT)(A) & 0x00ff0000) >> 8)   | \
	(( (UINT)(A) & 0x0000ff00) << 8)   | \
	(( (UINT)(A) & 0x000000ff) << 24))

// 16    
#define LittletoBig16(A)   (( ((USHORT)(A) & 0xff00) >> 8)    | \
	(( (USHORT)(A) & 0x00ff) << 8))
参照
http://blog.csdn.net/mycwq/article/details/18359007
http://www.erlang.org/doc/man/inet.html#setopts-2