Как открыть и использовать сокет в C? - PullRequest
16 голосов
/ 21 ноября 2008

Я хотел бы знать самый простой и эффективный способ открытия и записи данных в сокет на языке программирования C для сетевого программирования.

Ответы [ 8 ]

22 голосов
/ 21 ноября 2008

Вы правы, использование сокетов в C имеет сложный синтаксис. Более поздние языки, такие как Java и Python, делают сравнение несложным. Лучший учебник по программированию сокетов в C, который я нашел, - это Руководство Биджа по сетевому программированию . Я рекомендую начать с самого начала, чтобы получить хороший обзор, но если вам просто нужно, чтобы какой-то код работал сейчас , вы можете перейти к разделу, озаглавленному Клиент-сервер .

Удачи!

5 голосов
/ 21 ноября 2008

Вы не упоминаете, на какой платформе вы находитесь, но копия Сетевое программирование Unix Стивенса будет хорошим дополнением к вашей книжной полке. Большинство операционных систем реализуют сокеты Berkley с использованием сокетов, связывания, подключения и т. Д.

3 голосов
/ 21 ноября 2008

Если вы не напишите сетевой демон, большая часть работы в сети в C может быть выполнена на более высоком уровне, чем использование непосредственно сокетов с использованием соответствующих библиотек.

Например, если вы просто хотите получить файл с HTTP, используйте Neon или libcurl . Это будет проще, это будет на более высоком уровне, и у вас будут бесплатные SSL, IPv6 и т. Д.

2 голосов

POSIX 7 минимальный работающий клиентский сервер Пример TCP

Получить два компьютера в локальной сети.

Запустить сервер на одном компьютере с:

./server.out

Получите IP-адрес сервера с помощью ifconfig, например, 192.168.0.10.

На другом компьютере запустите:

./client.out 192.168.0.10

Теперь введите строки на клиенте, и сервер вернет их с шагом 1 (шифр ROT-1).

server.c

#define _XOPEN_SOURCE 700

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    int enable = 1;
    int i;
    int newline_found = 0;
    int server_sockfd, client_sockfd;
    socklen_t client_len;
    ssize_t nbytes_read;
    struct sockaddr_in client_address, server_address;
    unsigned short server_port = 12345u;

    if (argc > 1) {
        server_port = strtol(argv[1], NULL, 10);
    }

    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }

    server_sockfd = socket(
        AF_INET,
        SOCK_STREAM,
        protoent->p_proto
        /* 0 */
    );
    if (server_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");
        exit(EXIT_FAILURE);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(server_port);
    if (bind(
            server_sockfd,
            (struct sockaddr*)&server_address,
            sizeof(server_address)
        ) == -1
    ) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(server_sockfd, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    fprintf(stderr, "listening on port %d\n", server_port);

    while (1) {
        client_len = sizeof(client_address);
        client_sockfd = accept(
            server_sockfd,
            (struct sockaddr*)&client_address,
            &client_len
        );
        while ((nbytes_read = read(client_sockfd, buffer, BUFSIZ)) > 0) {
            printf("received:\n");
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n')
                newline_found;
            for (i = 0; i < nbytes_read - 1; i++)
                buffer[i]++;
            write(client_sockfd, buffer, nbytes_read);
            if (newline_found)
                break;
        }
        close(client_sockfd);
    }
    return EXIT_SUCCESS;
}

client.c

#define _XOPEN_SOURCE 700

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    char *server_hostname = "127.0.0.1";
    char *user_input = NULL;
    in_addr_t in_addr;
    in_addr_t server_addr;
    int sockfd;
    size_t getline_buffer = 0;
    ssize_t nbytes_read, i, user_input_len;
    struct hostent *hostent;
    /* This is the struct used by INet addresses. */
    struct sockaddr_in sockaddr_in;
    unsigned short server_port = 12345;

    if (argc > 1) {
        server_hostname = argv[1];
        if (argc > 2) {
            server_port = strtol(argv[2], NULL, 10);
        }
    }

    /* Get socket. */
    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Prepare sockaddr_in. */
    hostent = gethostbyname(server_hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
        exit(EXIT_FAILURE);
    }
    in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
    if (in_addr == (in_addr_t)-1) {
        fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
        exit(EXIT_FAILURE);
    }
    sockaddr_in.sin_addr.s_addr = in_addr;
    sockaddr_in.sin_family = AF_INET;
    sockaddr_in.sin_port = htons(server_port);

    /* Do the actual connection. */
    if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }
    while (1) {
        fprintf(stderr, "enter string (empty to quit):\n");
        user_input_len = getline(&user_input, &getline_buffer, stdin);
        if (user_input_len == -1) {
            perror("getline");
            exit(EXIT_FAILURE);
        }
        if (user_input_len == 1) {
            close(sockfd);
            break;
        }
        if (write(sockfd, user_input, user_input_len) == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }
        while ((nbytes_read = read(sockfd, buffer, BUFSIZ)) > 0) {
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n') {
                fflush(stdout);
                break;
            }
        }
    }
    free(user_input);

    exit(EXIT_SUCCESS);
}

На GitHub с Makefile . Протестировано на Ubuntu 15.10.

Длина сообщения

Вызовы read на клиенте и сервере выполняются внутри циклов while.

Как и при чтении из файлов, ОС может произвольно разделять сообщения, чтобы ускорить процесс, например, один пакет может прибыть намного раньше, чем другой.

Таким образом, протокол должен определить соглашение о том, где сообщения останавливаются Общие методы включают в себя:

  • заголовок с индикатором длины (например, HTTP Content-Length)
  • уникальная строка, которая завершает сообщения. Здесь мы используем \n.
  • сервер закрывает соединение: HTTP разрешает это https://stackoverflow.com/a/25586633/895245. Ограничено, конечно, поскольку следующее сообщение требует повторного соединения.

Следующие шаги

Этот пример ограничен, потому что:

  • сервер может обрабатывать только одно клиентское соединение одновременно
  • связь синхронизируется просто. Например: в приложении чата P2P сервер (другой человек) может отправлять сообщения в любое время.

Решение этих проблем требует многопоточности и, возможно, других вызовов, таких как poll.

0 голосов
/ 31 декабря 2010

Полное руководство по сетевому программированию в Linux описывает процесс сокетов легко и объясняет многие вещи, такие как многопоточность сервера, дизайн протокола и т. Д. Также хорошо подходят сокеты TCP / IP в C, второе издание.

0 голосов
/ 25 ноября 2008

Много полезных советов. Я обычно пишу на C ++, но вы можете найти какое-то применение в официальном документе, который я написал «Как избежать ошибок программирования в десятке сокетов» - игнорируйте совет по использованию инструментария ACE (так как он требует C ++), но обратите внимание на сокет ошибки в газете - их легко сделать и трудно найти, особенно для новичка. http://www.riverace.com/sockets10.htm

0 голосов
/ 21 ноября 2008

Вы можете попробовать Tcp4u, это бесплатно, что делает программирование сокетов очень простым.

http://www.jounin.net/tcp4u.html

0 голосов
/ 21 ноября 2008

Чтение и запись из основных сокетов не сложнее, чем чтение и запись обычных файлов (просто используйте recv вместо read и send вместо, если write). Вещи становятся немного хитрыми, когда вам нужно открыть сокет. Причина этого в том, что существует много разных способов связи с использованием сокетов (TCP, UDP и т. Д.).

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