2013-06-01 17 views
5

Câu hỏi này tương tự như Network port open, but no process attached?netstat shows a listening port with no pid but lsof does not. Nhưng câu trả lời cho họ không thể giải quyết được tôi, vì nó quá kỳ quặc.Tại sao luôn có 5 kết nối không có chương trình kèm theo?

Tôi có một ứng dụng máy chủ gọi lps mà chờ đợi cho các kết nối tcp trên cổng 8588.

[[email protected] lcms]# netstat -lnp | grep 8588 
tcp  0  0 0.0.0.0:8588    0.0.0.0:*     LISTEN   6971/lps 

Như bạn có thể thấy, không có gì là sai với ổ cắm nghe, nhưng khi tôi kết nối một số ngàn khách hàng kiểm tra (bằng văn bản bởi một đồng nghiệp khác) đến máy chủ, cho dù là 2000, 3000 hoặc 4000. Luôn có 5 khách hàng (cũng ngẫu nhiên) kết nối và gửi yêu cầu đăng nhập tới máy chủ, nhưng không thể nhận được bất kỳ phản hồi nào. Lấy 3000 khách hàng làm ví dụ. Đây là những gì lệnh netstat cho:

[[email protected] lcms]# netstat -nap | grep 8588 | grep ES | wc -l 
3000 

Và đây là lsof lệnh đầu ra:

[[email protected] lcms]# lsof -i:8588 | grep ES | wc -l 
2995 

Đó 5 kết nối đang ở đây:

[[email protected] lcms]# netstat -nap | grep 8588 | grep -v 'lps'     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52658   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52692   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52719   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52721   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52705   ESTABLISHED -     

5 ở trên cho thấy rằng họ đang kết nối đến máy chủ trên cổng 8588 nhưng không có chương trình nào được đính kèm. Và cột thứ hai (là RECV-Q) tiếp tục tăng khi khách hàng đang gửi yêu cầu.

Các liên kết ở trên nói điều gì đó về gắn kết NFS và RPC. Đối với RPC, tôi đã sử dụng lệnh rcpinfo -p và kết quả không liên quan gì đến cổng 8588. Và kết nối NFS, nfssta đầu ra cho biết Error: No Client Stats (/proc/net/rpc/nfs: No such file or directory).

Câu hỏi: Điều này có thể xảy ra như thế nào? Luôn luôn 5 và cũng không phải từ 5 khách hàng giống nhau. Tôi không nghĩ rằng đó là xung đột cổng như các khách hàng khác cũng được kết nối với cùng một máy chủ IP và cổng và tất cả chúng đều được xử lý đúng bởi máy chủ.

Lưu ý: Tôi đang sử dụng Linux epoll để chấp nhận yêu cầu của khách hàng. Tôi cũng viết mã gỡ lỗi trong chương trình của tôi và ghi lại mọi ổ cắm (cùng với thông tin của khách hàng) mà trả về accept nhưng không thể tìm thấy 5 kết nối. Đây là uname -a đầu ra:

Linux centos63 2.6.32-279.el6.x86_64 #1 SMP Fri Jun 22 12:19:21 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux 

Cảm ơn sự giúp đỡ của các bạn! Tôi thực sự bối rối.


Cập nhật 2013/06/08: Sau khi nâng cấp hệ thống để CentOS 6.4, cùng một vấn đề xảy ra. Cuối cùng tôi quay trở lại epoll và tìm thấy this page rằng bộ đó nghe fd không bị chặn và accept cho đến khi EAGAIN hoặc EWOULDBLOCK lỗi trả về. Và có, nó hoạt động. Không còn kết nối nào đang chờ xử lý. Nhưng tại sao vậy? Các Unix Mạng Lập trình Tập 1 nói

accept is called by a TCP server to return the next completed connection from the 
front of the completed connection queue. If the completed connection queue is empty, 
the process is put to sleep (assuming the default of a blocking socket). 

Vì vậy, nếu vẫn còn một số kết nối hoàn thành trong hàng đợi, tại sao quá trình này được đưa vào giấc ngủ?

Cập nhật 2013/07/01: tôi sử dụng EPOLLET khi thêm ổ cắm nghe, vì vậy tôi không thể chấp nhận tất cả nếu không giữ chấp nhận cho đến EAGAIN gặp phải. Tôi vừa mới nhận ra vấn đề này. Lỗi của tôi. Hãy nhớ rằng: luôn luôn read hoặc accept cho đến khi EAGAIN xuất hiện nếu sử dụng EPOLLET, ngay cả khi đó là ổ cắm nghe. Một lần nữa xin cảm ơn Matthew đã chứng minh cho tôi một chương trình thử nghiệm.

+0

Có điều gì đặc biệt về IP 192.168.0.241 trong môi trường của bạn không? – Nils

+0

Một thứ khác được thêm vào @Nils, tôi không nghĩ rằng đó là sự cố của IP 192.168.0.241. Chúng tôi có một số máy ảo thử nghiệm và 5 máy đó có thể đến từ các máy chủ khác nhau. – leowang

+0

Đợi một chút. Là máy chủ này 'lps' một chương trình bạn đang viết? –

Trả lời

1

tôi đã cố gắng sao chép vấn đề của bạn sử dụng các thông số sau:

  1. Các máy chủ sử dụng epoll để quản lý kết nối.
  2. Tôi tạo 3000 kết nối.
  3. Kết nối đang chặn.
  4. Máy chủ về cơ bản là 'giảm' để chỉ xử lý các kết nối và thực hiện rất ít công việc phức tạp.

Tôi không thể sao chép sự cố. Đây là mã nguồn máy chủ của tôi.

#include <stddef.h> 
#include <stdint.h> 
#include <stdbool.h> 
#include <stdlib.h> 
#include <stdio.h> 

#include <errno.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/epoll.h> 

#include <err.h> 
#include <sysexits.h> 
#include <string.h> 
#include <unistd.h> 

struct { 
    int numfds; 
    int numevents; 
    struct epoll_event *events; 
} connections = { 0, 0, NULL }; 

static int create_srv_socket(const char *port) { 
    int fd = -1; 
    int rc; 
    struct addrinfo *ai = NULL, hints; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_flags = AI_PASSIVE; 

    if ((rc = getaddrinfo(NULL, port, &hints, &ai)) != 0) 
    errx(EX_UNAVAILABLE, "Cannot create socket: %s", gai_strerror(rc)); 

    if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) 
    err(EX_OSERR, "Cannot create socket"); 

    if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) 
    err(EX_OSERR, "Cannot bind to socket"); 

    rc = 1; 
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0) 
    err(EX_OSERR, "Cannot setup socket options"); 

    if (listen(fd, 25) < 0) 
    err(EX_OSERR, "Cannot setup listen length on socket"); 

    return fd; 
} 

static int create_epoll(void) { 
    int fd; 
    if ((fd = epoll_create1(0)) < 0) 
    err(EX_OSERR, "Cannot create epoll"); 
    return fd; 
} 

static bool epoll_join(int epollfd, int fd, int events) { 
    struct epoll_event ev; 
    ev.events = events; 
    ev.data.fd = fd; 

    if ((connections.numfds+1) >= connections.numevents) { 
    connections.numevents+=1024; 
    connections.events = realloc(connections.events, 
     sizeof(connections.events)*connections.numevents); 
    if (!connections.events) 
     err(EX_OSERR, "Cannot allocate memory for events list"); 
    } 

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) { 
    warn("Cannot add socket to epoll set"); 
    return false; 
    } 

    connections.numfds++; 
    return true; 
} 

static void epoll_leave(int epollfd, int fd) { 
    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) < 0) 
    err(EX_OSERR, "Could not remove entry from epoll set"); 

    connections.numfds--; 
} 


static void cleanup_old_events(void) { 
    if ((connections.numevents - 1024) > connections.numfds) { 
    connections.numevents -= 1024; 
    connections.events = realloc(connections.events, 
     sizeof(connections.events)*connections.numevents); 
    } 
} 


static void disconnect(int fd) { 
    shutdown(fd, SHUT_RDWR); 
    close(fd); 
    return; 
} 

static bool read_and_reply(int fd) { 
    char buf[128]; 
    int rc; 
    memset(buf, 0, sizeof(buf)); 

    if ((rc = recv(fd, buf, sizeof(buf), 0)) <= 0) { 
    rc ? warn("Cannot read from socket") : 1; 
    return false; 
    } 

    if (send(fd, buf, rc, MSG_NOSIGNAL) < 0) { 
    warn("Cannot send to socket"); 
    return false; 
    } 

    return true; 
} 

int main() 
{ 
    int srv = create_srv_socket("8558"); 
    int ep = create_epoll(); 
    int rc = -1; 
    struct epoll_event *ev = NULL; 

    if (!epoll_join(ep, srv, EPOLLIN)) 
    err(EX_OSERR, "Server cannot join epollfd"); 

    while (1) { 
    int i, cli; 

    rc = epoll_wait(ep, connections.events, connections.numfds, -1); 
    if (rc < 0 && errno == EINTR) 
     continue; 
    else if (rc < 0) 
     err(EX_OSERR, "Cannot properly perform epoll wait"); 

    for (i=0; i < rc; i++) { 
     ev = &connections.events[i]; 

     if (ev->data.fd != srv) { 

     if (ev->events & EPOLLIN) { 
      if (!read_and_reply(ev->data.fd)) { 
      epoll_leave(ep, ev->data.fd); 
      disconnect(ev->data.fd); 
      } 
     } 

     if (ev->events & EPOLLERR || ev->events & EPOLLHUP) { 
      if (ev->events & EPOLLERR) 
      warn("Error in in fd: %d", ev->data.fd); 
      else 
      warn("Closing disconnected fd: %d", ev->data.fd); 

      epoll_leave(ep, ev->data.fd); 
      disconnect(ev->data.fd); 
     } 

     } 
     else { 

     if (ev->events & EPOLLIN) { 
      if ((cli = accept(srv, NULL, 0)) < 0) { 
      warn("Could not add socket"); 
      continue; 
      } 

      epoll_join(ep, cli, EPOLLIN); 
     } 

     if (ev->events & EPOLLERR || ev->events & EPOLLHUP) 
      err(EX_OSERR, "Server FD has failed", ev->data.fd); 

     } 
    } 

    cleanup_old_events(); 
    } 

} 

Đây là khách hàng:

from socket import * 
import time 
scks = list() 

for i in range(0, 3000): 
    s = socket(AF_INET, SOCK_STREAM) 
    s.connect(("localhost", 8558)) 
    scks.append(s) 

time.sleep(600) 

Khi chạy này trên máy tính địa phương của tôi tôi nhận được 6001 socket sử dụng cổng 8558 (1 nghe, 3000 socket client và 3000 socket server side).

$ ss -ant | grep 8558 | wc -l 
6001 

Khi kiểm tra số lượng kết nối IP kết nối trên máy khách tôi nhận được 3000.

# lsof -p$(pgrep python) | grep IPv4 | wc -l 
3000 

Tôi cũng đã cố gắng thử nghiệm với máy chủ trên một máy từ xa với thành công quá.

Tôi khuyên bạn nên thử làm như vậy.

Ngoài ra, hãy thử tắt hoàn toàn iptables trong trường hợp một số dấu vết theo dõi kết nối của nó. Đôi khi tùy chọn iptables trong /proc cũng có thể trợ giúp. Vì vậy, hãy thử sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1.

Chỉnh sửa: Tôi đã thực hiện một thử nghiệm khác để tạo ra kết quả bạn nhìn thấy bên cạnh bạn. Vấn đề của bạn là bạn đang tắt kết nối ở phía máy chủ trước emptively.

tôi có thể lặp lại kết quả tương tự với những gì bạn đang nhìn thấy cách làm như sau:

  • Sau khi đọc một số dữ liệu nhập vào máy chủ của tôi, hãy gọi shutdown(fd, SHUT_RD).
  • Làm send(fd, buf, sizeof(buf)) trên máy chủ.

Sau khi thực hiện việc này, bạn sẽ thấy các hành vi sau đây.

  • Trên máy khách, tôi nhận được 3000 kết nối mở trong netstat/ss với ESTABLISHED.
  • Trong đầu ra lsof tôi nhận được 2880 (bản chất của cách tôi đã tắt máy) kết nối được thiết lập.
  • Phần còn lại của các kết nối lsof -i:8558 | grep -v ES nằm trong CLOSE_WAIT.

Điều này chỉ xảy ra khi kết nối tắt máy.

Vì vậy, tôi nghi ngờ đây là lỗi trong chương trình máy khách hoặc máy chủ của bạn. Hoặc là bạn đang gửi một cái gì đó đến máy chủ mà máy chủ đối tượng hoặc máy chủ đóng kết nối không hợp lệ vì một lý do nào đó.

Bạn cần phải xác nhận rằng trạng thái kết nối "bất thường" trong (như close_wait hoặc một thứ gì đó khác).

Ở giai đoạn này, tôi cũng xem đây là vấn đề lập trình và không thực sự là thứ gì đó thuộc về serverfault. Nếu không nhìn thấy các phần liên quan của nguồn cho máy khách/máy chủ thì không ai có thể theo dõi nguyên nhân của lỗi. Mặc dù tôi khá tự tin rằng điều này không liên quan gì đến cách hệ điều hành xử lý các kết nối.

+0

Cảm ơn bạn đã dành thời gian để viết một chương trình thử nghiệm. Kết quả kiểm tra trên máy tính của tôi giống với kết quả của bạn. Tôi sửa đổi chương trình máy chủ của tôi trở lại để chặn nghe fd và 3000 kết nối cũng có thể được chấp nhận. Nhưng quay trở lại thói quen ban đầu với xử lý dữ liệu sắp tới, những kết nối bị mất mà không thể được chấp nhận trả về. Tôi cũng đã thử khi bạn đề nghị tắt iptables và sửa đổi tham số 'sysctl'. Vẫn không làm việc. – leowang

+0

Về mặt xử lý, nó là I/O, bộ nhớ hoặc CPU được nạp? –

+0

Tôi đã thêm bản cập nhật cho câu trả lời gốc của mình. Tôi cũng đã bỏ phiếu để di chuyển điều này sang stackoverflow vì nó có lẽ không còn thuộc về đây nữa. –

Các vấn đề liên quan