サーバ側 Packet Headerの処理

勢いだけで調べてみた.

以下はmysql-5.0.18/sql/net_serv.ccのmy_real_read関数の抜粋

uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : NET_HEADER_SIZE);

compress(zlib形式圧縮によるデータの送受信)を使用していなければremainに4が入る(NET_HEADER_SIZE = 4 defined by include/mysql_com.h).

uchar *pos;

pos = net->buff + net->where_b; /* net->packet -4 */
for (i=0 ; i < 2 ; i++)
{
  while (remain > 0)
  {
    /* First read is done with non blocking mode */
    if ((int) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L)

posには読み込んだデータを格納する先であるbuffの先頭アドレスが入る.compressを使用していなければnet->where_bは初期値0のままなので,vio_readにはbuffの先頭アドレスが渡される.remainは先ほど言ったように4.net->vioは"Vio"ないし"net_vio"構造体.次に出てくるvio->sdってのにmy_socketが入ってる.

以下はmysql-5.0.18/vio/viosocket.cのvio_read関数の"抜粋"

int vio_read(Vio * vio, gptr buf, int size)
{
  int r;
  r = read(vio->sd, buf, size);
  DBUG_RETURN(r);
}

readってのがmy_socket(= vio->sd)からsize分のバイトを読んでbufに入れてるのだと思う.つまりPacket Headerもbufに入れられたということ.

このbuffに対しては、再度mysql-5.0.18/sql/net_serv.ccのmy_real_read関数の抜粋(いろいろ削ってます).

ulong helping;
DBUG_DUMP("packet_header",(char*) net->buff+net->where_b, NET_HEADER_SIZE);
if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)
{
  if (net->buff[net->where_b] != (uchar) 255)
  {
    DBUG_PRINT("error", ("Packets out of order (Found: %d, expected %u)",
      (int) net->buff[net->where_b + 3], net->pkt_nr));
  }
  len= packet_error;
  net->report_error= 1;
  goto end;
}

len=uint3korr(net->buff+net->where_b);
if (!len)               /* End of big multi-packet */
  goto end;

helping = max(len,*complen) + net->where_b;
/* The necessary size of net->buff */
if (helping >= net->max_packet)
{
  if (net_realloc(net,helping))
  {
    len= packet_error;          /* Return error and close connection */
    goto end;
  }
}

pos=net->buff + net->where_b;
remain = (uint32) len;

end:
if (thr_alarm_in_use(&alarmed))
{
  my_bool old_mode;
  thr_end_alarm(&alarmed);
  vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
return(len);

まずbuffの4byte目がnet->pkt_nrに等しいかチェック.net->pkt_nrはこの場合0で初期化されている.Packet Headerの4byte目はPacketの連番を意味する.普通は0.いきなり1とかかで送るとここでエラー処理に入る.連番を1加算しつつ連続してPacketが送られてくる場合にはサーバ側もこのnet->pkt_nrを1加算して連番がマッチするかチェックというのがプロトコルから推測される順当な実装だが,実験でダンプしたPacketを見る限りでは加算されていなかった.この辺りの仕組みの解明はとりあえず今回は置いておく.

uint3korr関数では0xffffffを用いてAND演算を行った値を返す.つまり3byte目までに書かれたPacketのBodyの長さはこの関数の戻り値を用いて変数lenに格納される.

実験では16MB以上のデータ送信を行った際のPacket Headerは"00 00 00 00"であったわけだが,この場合,uint3korr関数の戻り値が0となる.len=0の場合にはここで"end"までジャンプする.

そうでない場合,例えば"SHOW VARIABLES"を実行した場合のPacket Headerは"0f 00 00 00"であるわけだが,この場合はlen=15となり,何かのチェックが行われてメモリ領域が足りない場合には再確保を行っているのかな? complenというのはcompressを使っている場合に絡んだ数値だから,恐らくzlib解凍した後に領域内に収まるか否かの話ではないかと推測する.

ここで言えること分かったことは,my_real_read関数の戻り値は実際に読んだ長さではなく,Packet Headerに書かれた長さを信用してそのまま戻り値として返しているということだ.

とりあえずここまで分かればいいや.今日はここでお仕舞い.Packetのダンプを別エントリで再度上げておきます.