2013-10-24 10 views
5

Ich brauche diesen Server in der Lage sein zu hören und neue Verbindungen mit Clients zu etablieren, während gleichzeitig in bestehende Verbindungen schreiben .. ie. Asynchroner nicht blockierender E/A. Mir wurde gesagt, dass ich poll() verwenden soll, aber nachdem ich eine übermäßige Zeit damit verbracht habe, Socket-Programmierung zu verstehen, bin ich immer noch unsicher, wie die poll() -Funktion implementiert werden soll.Implementieren von Poll() auf einem TCP-Server lesen/schreiben

int sockfd; 

int main(int argc, char *argv[]) 
{ 
int newsockfd, portno; 
socklen_t clilen; 
char buffer[256];  
struct sockaddr_in serv_addr, cli_addr; 
int n;    

if (argc < 2) {   
    fprintf(stderr,"ERROR, no port provided\n"); 
    exit(1); 
} 

sockfd = socket(AF_INET, SOCK_STREAM, 0);  
if (sockfd < 0)      
    error("ERROR opening socket");   

bzero((char *) &serv_addr, sizeof(serv_addr));  

portno = atoi(argv[1]);    
serv_addr.sin_family = AF_INET;   
serv_addr.sin_addr.s_addr = INADDR_ANY;  
serv_addr.sin_port = htons(portno);  

if (bind(sockfd, (struct sockaddr *) &serv_addr, 
      sizeof(serv_addr)) < 0) 
      error("ERROR on binding");   
listen(sockfd,5);     

clilen = sizeof(cli_addr);  

while(1){   
    newsockfd = accept(sockfd, 
       (struct sockaddr *) &cli_addr, 
       &clilen);    
    if (newsockfd < 0)     
      error("ERROR on accept"); 

    // READ READ READ READ READ READ READ READ READ READ READ READ READ READ READ READ 
    bzero(buffer,256); 
    n = read(newsockfd,buffer,255);  


    if (n < 0) error("ERROR reading from socket"); 
    printf("Here is the message: %s\n",buffer); 

    // WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE 
    n = write(newsockfd,"I got your message",18); 
    if (n < 0) error("ERROR writing to socket"); 
    close(newsockfd); 
} 

return 0; 

}

Mein Verständnis ist, dass ich so etwas bauen müssen:

// Set up array of file descriptors for polling 
struct pollfd ufds[2]; 
ufds[0].fd = sockfd;  
ufds[0].events = POLLIN; 

ufds[1].fd = newsockfd; 
ufds[1].events = POLLOUT; 

und Nutzung Umfrage (UFDs, 2,2000); innerhalb der Schleife, um zu überprüfen, ob sockfd oder newsockfd irgendeine Aktivität haben, in diesem Fall benutze ich das entsprechende lesen oder schreiben .. Wenn jemand mir eine Führung geben könnte, würde ich sehr dankbar sein.

+1

Haben Sie über "select" gelesen? – NiRR

Antwort

3

Der Kernel füllt die Ereignisse aus, die im Feld revents Ihres Arrays struct pollfd aufgetreten sind.

Aus der Handbuch Seite:

Das Feld revents ist ein Ausgabeparameter, durch den Kernel mit den Ereignissen gefüllt, die tatsächlich aufgetreten ist. Die in revents zurückgegebenen Bits können alle in Ereignissen angegebenen Werte oder einen der Werte POLLERR, POLLHUP oder POLLNVAL enthalten. (Diese drei Bits sind bedeutungslos im Veranstaltungsbereich und wird im revents Feld gesetzt werden, wenn die entsprechende Bedingung erfüllt ist.)

Wenn Sie Ereignisbenachrichtigungen für angenommene Verbindungen wollen, dann müssen Sie entweder Reserveraum in Erhöhen oder ändern Sie das Array struct pollfd für jede Verbindung.

Sie benötigen eine Möglichkeit, den hörenden Sockel zu unterscheiden. Sie könnten es im Index Null Ihres Arrays speichern.

int i, n; 

n = poll(ufds, num_fds_in_array, timeout_value); 

/* errors or timeout? */ 
if (n < 1) 
    ; 

for (i = 0; i < num_fds_in_array; i++) { 
    /* were there any events for this socket? */ 
    if (!ufds[i].revents) 
     continue; 

    /* is it our listening socket? */ 
    if (!i) { 
     if (ufds[0].revents & POLLIN) 
      /* call accept() and add the new socket to ufds */ 
     else 
      /* error */ 

     continue; 
    } 

    /* is there incoming data on the socket? */ 
    if (ufds[i].revents & POLLIN) 
     /* call recv() on the socket and decide what to do from there */ 
} 

Das wird POLLOUT Flag verwendet, um den Anrufer zu signalisieren, wenn die Sendedaten auf dem Sockel nicht blockieren.

Für nicht blockierende E/A würde ich eine leistungsfähigere API verwenden, da es mehr Buchhaltung erfordert, um zuverlässig zu arbeiten. Siehe den nächsten Absatz.

Leider gibt es keinen Platz für Hilfsdaten pro Verbindung, um den Zustand zu speichern, wenn poll verwendet wird. Abhängig von Ihrer Plattform sind Alternativen verfügbar, z. G. epoll für Linux, kqueue für * BSD und eine Handvoll Optionen für Windows. Wenn Sie poll mit Kontextdaten verwenden möchten, müssen Sie eine Datenstruktur verwenden, die mit dem Dateideskriptor oder dem Array-Index durchsucht werden kann.

+0

Ich würde mich sehr freuen, wenn Sie zeigen würden, wie dieser Code auf den OP-Code angewendet werden kann. Ich habe genau das gleiche Problem, das ich gelesen habe() Befehle, die korrekt blockieren, aber ich möchte ein Verbindungstimeout haben. Ich habe so viele Dinge ausprobiert, aber es funktioniert nicht. Ich verstehe deinen Code nicht ... Was ist ufds? Ist es newsockfd? –