Клиент / серверные сокеты в c - PullRequest
1 голос
/ 10 мая 2011

В настоящее время я пытаюсь понять, как использовать сокеты для создания клиент-серверной программы на языке C. Я читал различные учебные пособия по Интернету в надежде попробовать создать небольшой эхо-сервер, который может работать с несколькими клиентами на в то же время. Всякий раз, когда клиент отправляет серверу сообщение, предполагается, что сервер возвращает эхо-сообщение клиенту. Код, который я создал, представляет собой сочетание учебника из лекции в школе (в котором объясняется, как создать клиент и сервер), а также пример, который я нашел здесь по stackoverflow (который показал, как заставить функции фактически отображаться сообщения). Я надеюсь, что кто-то может объяснить мне, чего мне не хватает, чтобы эта программа работала правильно. Вот код клиента:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

char buf[80];
struct sockaddr myname;

void replyBack(FILE *fp, int sockfd) {
char sendline[1000], recvline[1000];
printf("Enter your echo: \n");
while(fgets(sendline,1000,stdin) != NULL) {
    write(sockfd,sendline,sizeof(sendline));
    if(read(sockfd,recvline,1000) == 0) {
        printf("str_cli: server terminated prematurely");
        exit(-1);
    }
    fputs(recvline, stdout);
}
}

main() {
int sock, adrlen, cnt;

sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
    printf("client socket failure%d\n", errno);
    printf("client: ");
    exit(1);
}

myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

if(connect(sock, &myname, adrlen) < 0) {
    printf("client connect failure %d\n", errno);
    perror("client: ");
    exit(1);
}

replyBack(stdin,sock);
exit(0);
}

А вот код сервера:

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

struct sockaddr myname;
char buf[80];

void echo(int sockfd) {
ssize_t n;
int write_err;
char buf[1000];
char * send_start_pos;
while(1) {
    bytes_in = read(sockfd, buf, 1000);
    if(bytes_in < 1) {
        if(errno == EINTR)
            continue;
        break;
    }
    bytes_remaining = bytes_in;
    send_start_pos = buf;
    write_err = 0;

    while((bytes_remaining > 0) && !(write_err)) {
        bytes_out = write(sockfd, send_start_pos,           
                bytes_remaining);
        if(bytes_out < 0) {
            if(errno == EINTR)
                continue;
            write_err = 1;
            break;
        }
    bytes_remaining -= bytes_out;
    send_start_pos += bytes_out;
    }
    if(write_err)
        break;
}
}

main() {
int sock, new_sd, adrlen, cnt;

sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
    printf("server socket failure %d\n", errno);
    perror("server: ");
    exit(1);
}

myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

unlink("/tmp/billb"); /*defensive programming */
if(bind(sock, &myname, adrlen) < 0) {
    printf("server bind failure%d\n", errno);
    perror("server: ");
    exit(1);
}

if(listen(sock, 5) < 0) {
    printf("server listen failure %d\n", errno);
    perror("server: ");
    exit(1);
}

while(1) {
    if(new_sd = accept(sock, &myname, &adrlen) < 0) {
        printf("server accept failure %d\n", errno);
        perror("server: ");
        exit(1);
    }

    printf("Socket address in server %d is %s, %s\n", 
        getpid(), myname.sa_data, myname.sa_data);

    if(fork() == 0) {
        close(sock);
        echo(new_sd);
        exit(0);
    }
    close(new_sd);
}
}

Проблема при запуске программы заключается в том, что клиент принимает сообщение, но фактически не отправляет его на сервер, поэтому сервер никогда не сможет вернуть его обратно.

Я знаю, что это, вероятно, основные вещи, поэтому я ценю ваше терпение и время!

Ответы [ 2 ]

3 голосов
/ 10 мая 2011

Вам не хватает грузовика включает в себя.Я потратил время на компиляцию ваших программ, и этот драгоценный камень выделялся из толпы предупреждений

warning: suggest parentheses around assignment used as truth value

В этой строке

if(new_sd = accept(sock, &myname, &adrlen) < 0) {

Итак, в основном вы проверяете, возвращает ли accept что-то меньшеечем 0 и присвоение этому new_sd.Измените эту строку на

if((new_sd = accept(sock, &myname, &adrlen)) < 0) {

Бонус : большая часть того, что @sarnold сказал в своем ответе, верна и в конечном итоге сделает вас лучше, чем мой ответ.

2 голосов
/ 10 мая 2011
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

Это должно быть sizeof myname. Сокеты unix(7) на самом деле определены как имеющие sockaddr_un, например:

       #define UNIX_PATH_MAX    108

       struct sockaddr_un {
           sa_family_t sun_family;               /* AF_UNIX */
           char        sun_path[UNIX_PATH_MAX];  /* pathname */
       };

Вы дали 10+4 в качестве размера. Который должен почти работать - он не считает завершающий байт NUL в конце имени файла - но даже с добавленным + 1, я бы чувствовал себя лучше, если вы передаете точно размер объекта у тебя есть. (Будьте осторожны с strlen(3) в строках. Почти всегда вам нужен + 1 в любом выражении, которое включает strlen(3). Забавно.)

while(fgets(sendline,1000,stdin) != NULL) {
    write(sockfd,sendline,sizeof(sendline));

И здесь вы отправляете все 1000 байтов вашего буфера, независимо от содержимого. Пользователь мог ввести один символ или сотню, и вы отправляете все 1000 байтов того, что находится по этому адресу.

Ваш метод echo немного сложен; Я бы порекомендовал разделить запись в ее собственную рутину; Расширенное программирование в среде Unix, 2-е издание (книга превосходная , которую стоит найти, если вы планируете программировать Unix или Unix-подобные системы), имеет небольшую приятную процедуру наслаждайтесь:

ssize_t             /* Write "n" bytes to a descriptor  */
writen(int fd, const void *ptr, size_t n)
{
    size_t      nleft;
    ssize_t     nwritten;

    nleft = n;
    while (nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) < 0) {
            if (nleft == n)
                return(-1); /* error, return -1 */
            else
                break;      /* error, return amount written so far */
        } else if (nwritten == 0) {
            break;
        }
        nleft -= nwritten;
        ptr   += nwritten;
    }
    return(n - nleft);      /* return >= 0 */
}

(См. lib/writen.c в источнике с сайта книги .)

На самом деле я не пробовал ваш код, поэтому вполне возможно, что ни одна из них не является проблемой, фактически препятствующей работе вашего программного обеспечения :), если это так, оставьте комментарий, и я приму Присмотритесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...