2010-03-30 9 views
6

भेजें() का man pageMSG_MORE ध्वज बताता है जिसे TCP_CORK जैसा कार्य करने के लिए कहा जाता है। मैं send() के चारों ओर एक आवरण समारोह है:'MSG_MORE`- फ़्लैग किए गए पैकेट्स के लिए फ्लश कर्नेल का टीसीपी बफर

int SocketConnection_Write(SocketConnection *this, void *buf, int len) { 
    errno = 0; 

    int sent = send(this->fd, buf, len, MSG_NOSIGNAL); 

    if (errno == EPIPE || errno == ENOTCONN) { 
     throw(exc, &SocketConnection_NotConnectedException); 
    } else if (errno == ECONNRESET) { 
     throw(exc, &SocketConnection_ConnectionResetException); 
    } else if (sent != len) { 
     throw(exc, &SocketConnection_LengthMismatchException); 
    } 

    return sent; 
} 

मान लिया जाये कि मैं कर्नेल बफर का उपयोग करना चाहते हैं, मैं TCP_CORK साथ जा सकते हैं, सक्षम जब भी यह आवश्यक है और उसके बाद उसे निष्क्रिय बफर फ्लश करने के लिए। लेकिन दूसरी तरफ, एक अतिरिक्त सिस्टम कॉल की आवश्यकता उत्पन्न होती है। इस प्रकार, MSG_MORE का उपयोग मेरे लिए अधिक उपयुक्त लगता है। मैं बस के ऊपर भेजने() लाइन को बदल देंगे:

int sent = send(this->fd, buf, len, MSG_NOSIGNAL | MSG_MORE); 

lwm.net के अनुसार, पैकेट स्वचालित रूप से प्लावित हो जाएगा वे इतने बड़े हैं कि यदि: एक आवेदन एक पर उस विकल्प सेट

हैं सॉकेट, कर्नेल छोटे पैकेट नहीं भेजेगा। इसके बजाय, यह का इंतजार करेगा जब तक कि पर्याप्त डेटा को अधिकतम आकार के पैकेट को भरने के लिए दिखाया गया हो, फिर उसे भेजें। जब TCP_CORK बंद हो जाता है, तो शेष डेटा तार पर बाहर जाएगा।

लेकिन यह अनुभाग केवल TCP_CORK को संदर्भित करता है। अब, MSG_MORE पैकेट फ्लश करने का सही तरीका क्या है?

मैं केवल दो संभावनाएं के बारे में सोच सकते हैं: this पृष्ठ पर वर्णित के रूप में

  1. कॉल भेजें() एक खाली बफर के साथ और MSG_MORE
  2. TCP_CORK विकल्प पुन: लागू स्थापित किया जा रहा बिना

दुर्भाग्य से पूरा विषय बहुत खराब दस्तावेज है और मुझे इंटरनेट पर बहुत कुछ नहीं मिला।

मैं यह भी सोच रहा हूं कि सबकुछ जांचने के लिए कैसे काम करता है? जाहिर है strace के माध्यम से सर्वर चलाना एक विकल्प नहीं है। तो सबसे आसान तरीका netcat का उपयोग करना होगा और उसके बाद strace आउटपुट देखें? या कर्नेल एक लूपबैक इंटरफ़ेस पर अलग-अलग ट्रैफिक को संभालता है?

+0

sendfile() 'MSG_MORE' ध्वज को संरक्षित करता है। Sendfile() रिटर्न जब कैश तब फ्लश किया जाता है। – user206268

उत्तर

11

मैंने कर्नेल स्रोत पर एक नज़र डाली है और दोनों मान्यताओं को सत्य प्रतीत होता है। निम्नलिखित कोड net/ipv4/tcp.c (2.6.33.1) से निष्कर्ष हैं।

static inline void tcp_push(struct sock *sk, int flags, int mss_now, 
       int nonagle) 
{ 
    struct tcp_sock *tp = tcp_sk(sk); 

    if (tcp_send_head(sk)) { 
     struct sk_buff *skb = tcp_write_queue_tail(sk); 
     if (!(flags & MSG_MORE) || forced_push(tp)) 
      tcp_mark_push(tp, skb); 
     tcp_mark_urg(tp, flags, skb); 
     __tcp_push_pending_frames(sk, mss_now, 
         (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle); 
    } 
} 

इसलिए, अगर झंडा नहीं सेट, लंबित फ्रेम निश्चित रूप से प्लावित हो जाएगा। लेकिन इस केवल मामला हो जब बफर खाली नहीं है है:

static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, 
      size_t psize, int flags) 
{ 
(...) 
    ssize_t copied; 
(...) 
    copied = 0; 

    while (psize > 0) { 
(...) 
     if (forced_push(tp)) { 
      tcp_mark_push(tp, skb); 
      __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH); 
     } else if (skb == tcp_send_head(sk)) 
      tcp_push_one(sk, mss_now); 
     continue; 

wait_for_sndbuf: 
     set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); 
wait_for_memory: 
     if (copied) 
      tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); 

     if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) 
      goto do_error; 

     mss_now = tcp_send_mss(sk, &size_goal, flags); 
    } 

out: 
    if (copied) 
     tcp_push(sk, flags, mss_now, tp->nonagle); 
    return copied; 

do_error: 
    if (copied) 
     goto out; 
out_err: 
    return sk_stream_error(sk, flags, err); 
} 

while पाश के शरीर क्योंकि psize तो नहीं अधिक से अधिक 0. है कभी नहीं निष्पादित किया जाएगा, out अनुभाग में, वहाँ एक और मौका है, tcp_push() को बुलाया जाता है लेकिन copied के पास अभी भी इसका डिफ़ॉल्ट मान है, यह भी असफल हो जाएगा।

तो लंबाई 0 के साथ एक पैकेट भेजना कभी भी फ्लश नहीं होगा।

अगला सिद्धांत TCP_CORK को फिर से लागू करना था। आइए पहले कोड को देखें:

static int do_tcp_setsockopt(struct sock *sk, int level, 
     int optname, char __user *optval, unsigned int optlen) 
{ 

(...) 

    switch (optname) { 
(...) 

    case TCP_NODELAY: 
     if (val) { 
      /* TCP_NODELAY is weaker than TCP_CORK, so that 
      * this option on corked socket is remembered, but 
      * it is not activated until cork is cleared. 
      * 
      * However, when TCP_NODELAY is set we make 
      * an explicit push, which overrides even TCP_CORK 
      * for currently queued segments. 
      */ 
      tp->nonagle |= TCP_NAGLE_OFF|TCP_NAGLE_PUSH; 
      tcp_push_pending_frames(sk); 
     } else { 
      tp->nonagle &= ~TCP_NAGLE_OFF; 
     } 
     break; 

    case TCP_CORK: 
     /* When set indicates to always queue non-full frames. 
     * Later the user clears this option and we transmit 
     * any pending partial frames in the queue. This is 
     * meant to be used alongside sendfile() to get properly 
     * filled frames when the user (for example) must write 
     * out headers with a write() call first and then use 
     * sendfile to send out the data parts. 
     * 
     * TCP_CORK can be set together with TCP_NODELAY and it is 
     * stronger than TCP_NODELAY. 
     */ 
     if (val) { 
      tp->nonagle |= TCP_NAGLE_CORK; 
     } else { 
      tp->nonagle &= ~TCP_NAGLE_CORK; 
      if (tp->nonagle&TCP_NAGLE_OFF) 
       tp->nonagle |= TCP_NAGLE_PUSH; 
      tcp_push_pending_frames(sk); 
     } 
     break; 
(...) 

जैसा कि आप देख सकते हैं, फ्लश करने के दो तरीके हैं। आप या तो TCP_NODELAY पर 1 या TCP_CORK से 0 पर सेट कर सकते हैं। सौभाग्य से, दोनों जांच नहीं करेंगे कि ध्वज पहले से सेट है या नहीं। इस प्रकार, TCP_CORK ध्वज को फिर से लागू करने की मेरी प्रारंभिक योजना को इसे अक्षम करने के लिए अनुकूलित किया जा सकता है, भले ही यह वर्तमान में सेट न हो।

मुझे आशा है कि यह किसी को भी इसी तरह के मुद्दों के साथ मदद करेगा।

+0

इस शोध के लिए धन्यवाद। बहुत उपयोगी। – bvanderveen

3

अनुसंधान के एक बहुत है कि ... सब मैं पेशकश कर सकते हैं इस अनुभवजन्य पोस्ट टिप्पणी है:

MSG_MORE सेट के साथ पैकेट का एक समूह, MSG_MORE बिना एक पैकेट के बाद भेजा जा रहा है, बहुत बाहर चला जाता है। यह कुछ इस तरह के लिए एक इलाज काम करता है:

for (i=0; i<mg_live.length; i++) { 
     // [...] 
     if ((n = pth_send(sock, query, len, MSG_MORE | MSG_NOSIGNAL)) < len) { 
      printf("error writing to socket (sent %i bytes of %i)\n", n, len); 
      exit(1); 
     } 
    } 
    } 

    pth_send(sock, "END\n", 4, MSG_NOSIGNAL); 

है यही कारण है कि आप एक बार में सभी पैकेट भेजने जब रहे हैं, और एक स्पष्ट रूप से परिभाषित अंत है ... और आप केवल एक सॉकेट प्रयोग कर रहे हैं।

यदि आपने उपरोक्त लूप के बीच में किसी अन्य सॉकेट को लिखने का प्रयास किया है, तो आप पाएंगे कि लिनक्स पहले से आयोजित पैकेट जारी करता है। कम से कम ऐसा लगता है कि मैं अभी परेशानी कर रहा हूं। लेकिन यह आपके लिए एक आसान समाधान हो सकता है।