Программирование сервера сокетов

#c #function #sockets #unix #networking

#c #функция #сокеты #unix #сеть

Вопрос:

Я попытался создать серверную программу, которая отправляет и получает сообщения от клиента. Я хочу поместить инструкции чтения и записи в функции, потому что я хочу повторно использовать код (затем я должен отправить и получить больше сообщений). Сервер работает корректно, но когда я помещаю инструкции по записи в writeSocket(), программа запускает SIGPIPE, и я не знаю почему. Я пытался также использовать функцию readSocket() (просто скопируйте инструкции по чтению в функцию), но у меня была та же проблема. Не могли бы вы мне помочь, пожалуйста?

Это код моей серверной программы:

 #include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#define MAXSIZE 256
int isTerminated(char buffer[], int len);
void writeSocket(int sock, char buffer[]);

int main(){
    struct sockaddr_in Local;
    struct sockaddr_in Client;
    short int port=1745;

    char buffer[MAXSIZE];
    int sockfd, newsockfd, n, nread, nwrite, len;
    int turn_off=0;//Controllare le impostazioni del contatore

    sockfd=socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd<0){
        fprintf(stderr, "Server: Socket creating failed!n");
        return -1;
    }else
        printf("Server: Socket created!n");
    memset(amp;Local, 0, sizeof(Local));
    Local.sin_family=AF_INET;
    Local.sin_addr.s_addr=htonl(INADDR_ANY);
    Local.sin_port=htons(port);
    
    if(bind(sockfd, (struct sockaddr*)amp;Local, sizeof(Local))<0){
        fprintf(stderr, "Server: Binding socket failed!n");
        return -1;
    }else
        printf("Server: Bind ok!n");

    if(listen(sockfd, 10)<0){
        fprintf(stderr, "Server: Listen failed!n");
        return -1;
    }else
        printf("Server: Listen ok!n");

    while(1){
        buffer[0]='';
        printf("Server: Waiting for connection...n");
        newsockfd=accept(sockfd, (struct sockaddr*)amp;Client, amp;len);
        printf("Server: Connection accepted!n");


        nread=0;
        while((n=read(newsockfd, amp;(buffer[nread]), MAXSIZE))>0){
            nread =n;
            printf("Server: Ho letto %d caratteri con %d totali!n", n, nread);
            if(isTerminated(buffer, nread))//Fine stringa
                break;
        }
        buffer[nread]='';


        printf("Ho ricevuto il messaggio %sn", buffer);
        if(!strcmp(buffer, "Turn OFF")){
            buffer[0]='';
            strcpy(buffer, "Counter OFF");
            turn_off=1;
        }else{
            buffer[0]='';
            strcpy(buffer, "OK");
        }

        /*nwrite=0;
        buffer[strlen(buffer)]='';
        int str_len=strlen(buffer) 1;
        while((str_len>nwrite) amp;amp; (n=write(newsockfd, amp;(buffer[nwrite]), str_len-nwrite)>0))
            nwrite =n;*/
        writeSocket(sockfd, buffer);

        close(newsockfd);
        if(turn_off)//Spegni il contatore e salva la sessione
            break;
    }
    close(sockfd);

    return 0;
}

void writeSocket(int sock, char buffer[]){
    buffer[strlen(buffer)]='';
    int str_len=strlen(buffer) 1;
    int nwrite=0;
    int n;
    printf("Devo inviare il messaggio: %sn", buffer);
    printf("Valore n: %dn", sock);
    while((str_len>nwrite) amp;amp; (n=write(sock, amp;(buffer[nwrite]), str_len-nwrite)>0))
        nwrite =n;
    
}

int isTerminated(char buffer[], int len){
    int i;
    for(i=0; i<=len; i  )
        if(buffer[i] == '')
            return 1;
    return 0;
}
  

Комментарии:

1. SIGPIPE при записи означает, что другой конец закрыл соединение.

2. Вы не можете использовать sockfd для связи; это прослушивающий сокет. Используйте клиентский сокет newsockfd .

3. Если другой конец сокета закроет соединение, то read вернется 0 . Вам нужно проверить это.

4. writeSocket не обрабатывает возврат -1 (указывающий на ошибку) так, как это делает ваш цикл чтения сокета. Кроме того, вам может потребоваться включить повторное использование сокета. Добавьте это перед вашим bind вызовом: int enable = 1; int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, amp;enable, sizeof(enable));

5. @CraigEstey ИМХО, использование SO_REUSEADDR — плохая практика, если вам это действительно не нужно (например, многопроцессорный сервер). Обычно вы не хотите привязываться к уже используемому порту. Если он уже используется, возникает проблема.