吐槽:memcached伺服器不搭理來自nc的set命令!

來源:互聯網
上載者:User

原文地址:http://blog.csdn.net/ffb/article/details/17437101

使用nc訪問memcache服務的時候,發現set命令總是失敗,但是其他命令可用。換telnet一切正常,疑似伺服器bug,值得研究。

nc使用的命令是:

nc 127.0.0.1 11212
telnet使用的命令是:
telnet 127.0.0.1 11212
於是嘗試使用wireshark抓包分析資料差異,

telnet的請求資料如下:

0000   73 65 74 20 70 79 77 20 31 20 30 20 33 0d 0a 31  set pyw 1 0 3..10010   30 30 0d 0a                                      00..

nc的請求資料如下:

0000   73 65 74 20 70 79 77 20 31 20 30 20 33 0a        set pyw 1 0 3.0000   31 30 30 0a                                      100.

發現nc發送的斷行符號符是0A,telnet發送的斷行符號符是0D 0A,應該是這個差異導致的問題,但是nc發送其他命令都可以正確執行,更加懷疑伺服器代碼有bug。

於是查看原始碼,處理命令格式的代碼在:

memcached.c::try_read_command

2455     cont = el + 1;                                                                                           2456     if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {                                                          2457         el--;                                                                                                2458     }                                                                                                        2459     *el = '\0';

其中,el是

2433     el = memchr(c->rcurr, '\n', c->rbytes);

看似代碼沒啥問題。於是向上看,讀取命令和判斷的位置在drive_machine(),其中c->state是用來控制處理流程,所以只要跟蹤這個變數的變化即可。於是跟蹤:

$ gdb ./memcached(gdb) set args -A 11212(gdb) b try_read_commandBreakpoint 1 at 0x409e79: file memcached.c, line 2422.(gdb) r

用戶端輸入

get pywENDset pyw 0 0 3100
可以看出,對於來自ns的set命令,try_read_command之後,狀態值變為conn_nread而不是conn_mwrite,意思是還需要繼續讀取命令參數。所以增加conn_nread的斷點

(gdb) b 2848

調試之後看到了這段代碼:

2848             if (c->rlbytes == 0) {                                                                           2849                 complete_nread(c);                                                                                                          2850                 break;                                                                                       2851             }
跟進去,就發現了非常可疑的代碼:
948     if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) {

修改為

948     if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0 && strncmp(ITEM_data(it) + it->nbytes - 1, "\n", 1) != 0) {
編譯運行,重新執行上面的命令,多斷行符號一次,探索資料成功的儲存了。 

而為什麼要多斷行符號一次,是因為memcache伺服器端整個狀態的流轉,都是靠已經讀取的位元組數來驅動的,當使用者輸入了set命令,會由process_update_command函數設定一個期望接收的位元組數到變數conn::rlbytes,而這個期望數值預設是按照\r\n來計算的,比如希望再輸入三個位元組,這個值就會被填寫為5。

由於代碼中多處處理方法都和\r\n高度綁定,比如item_make_header(),如果想徹底解決這個問題,最好是在首個命令之後分析終端的斷行符號符類型,統一標識,在標識的基礎上對相關的代碼進行相容性改進。但是由於涉及的修改比較多,本次研究就到這裡了。

在結束前,我又換了個思路。這裡涉及的問題實際是memcache的通訊協議規定了\r\n是標準的命令結束方法,所以伺服器端代碼實際上不需要進行任何修改,只要用戶端按照協議要求發送\r\n作為斷行符號即可(在改變之前不妨先看看是否可以遵守),man了一下nc,嘗試如下命令:

$ nc -C 127.0.0.1 11211set pyw 1 0 3100STORED
OK,問題解決。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.