Программирование сокетов для начинающих на C

#c #sockets #client

#c #сокеты #клиент

Вопрос:

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

Но теперь я думал о том, чтобы иметь два компьютера и делать это.. У меня есть следующие вопросы

  1. Допустим, один компьютер является сервером, а другой — клиентом. Теперь, должен ли серверный код находиться на серверном компьютере, а клиентский код — на клиентском?
  2. В коде сервера, когда мы указываем ip-адрес для bind() , это должен быть ip-адрес системы, который мы можем найти через ipconfig , или он все еще должен оставаться адресом обратной связи?
  3. В клиентском коде, я полагаю, IP-адрес назначения должен быть адресом серверного компьютера, верно??
  4. И последнее, и самое важное, КАК мне ПОДКЛЮЧИТЬ ДВА КОМПЬЮТЕРА??

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

СЕРВЕРНЫЙ КОД

 #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define MYPORT 3500

int main()
{
    int sockfd;
    int clientfd;
    int bytes_read;
    char buf[100];
    int struct_size;
    struct sockaddr_in my_addr;
    struct sockaddr_in con_addr;
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(MYPORT);
    my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    my_addr.sin_zero[8]='';

    bind(sockfd, (struct sockaddr*)amp;my_addr, sizeof(struct sockaddr));

    listen(sockfd,5);
    
    struct_size = sizeof(con_addr);
    clientfd = accept(sockfd, (struct sockaddr*)amp;con_addr, amp;struct_size);

    bytes_read = read(clientfd, buf, 100);
    buf[bytes_read] = '';
    printf("Message from client:%d is %s n",clientfd, buf);

    close(sockfd);
    close(clientfd);
}
  

КЛИЕНТСКИЙ КОД

 #include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<string.h>
#include<stdio.h>

#define DESTPORT 3500

int main()
{

    struct sockaddr_in dest_addr;
    
    int sockfd = socket(AF_INET,SOCK_STREAM,0);

    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(DESTPORT);
    dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    dest_addr.sin_zero[8]='';

    connect(sockfd,(struct sockaddr*)amp;dest_addr, sizeof(struct sockaddr));

    char msg[100];
    printf("Enter you message: ");
    gets(amp;msg); 
    
    int w = write(sockfd, msg, strlen(msg));
    
    close(sockfd);
    printf("Client Dying.....n"); 

    return 0;
}
  

Ответ №1:

1) Правильно.

2) На стороне сервера вы можете привязаться к 0.0.0.0, что означает «все интерфейсы (IPv4)».

3) Да, вы правы.

4) Чаще всего через коммутатор Ethernet (или перекрестный кабель Ethernet, но их сложнее найти).

Ответ №2:

Сервер должен bind использовать 0.0.0.0 (любой), если вы не пытаетесь ограничить доступ (и в этом случае вам действительно следует использовать брандмауэр, а не привязку к порту). Правильный способ на самом деле:

 struct addrinfo *ai, hints = { .ai_flags = AI_PASSIVE };
if (getaddrinfo(0, "1234", amp;hints, amp;ai)) goto error;
int fd = socket(ai->ai_family, SOCK_STREAM, 0);
bind(fd, ai->ai_addr, ai->ai_addrlen);
  

Добавьте, конечно, некоторую проверку ошибок. Замените «1234» номером вашего порта.

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

1. Решает мой случай именно так, как я этого хотел.. Огромное спасибо 🙂 Можете ли вы предложить какой-нибудь хороший ресурс, чтобы прочитать о современном программировании сокетов??

2. В основном справочные страницы (или документация POSIX, здесь: pubs.opengroup.org/onlinepubs/9699919799/functions / … ) для getaddrinfo , getnameinfo socket bind , connect listen accept , sendto recvfrom , select ,,,,,,,,,,,,,,,,,,,,,,, и,,, это практически все, что вам нужно. Вся сложность, которую вы видите в старых примерах и руководствах, — это просто унаследованный багаж и ущерб поддержке IPv6 и переносимости.

3. Почему вы нигде не указываете порт?

4. Глупая ошибка. Исправлено. Конечно, вы могли бы позволить bind присвоить вам номер порта, а затем использовать getsockname и getnameinfo для его записи и публикации для использования клиентами.

5. При использовании getaddrinfo() вы всегда должны перебирать все результаты и создавать по одному сокету на каждый адрес прослушивания. Если вы просто хотите использовать IPv4 для любого адреса для простоты, нет причин использовать getaddrinfo() в первую очередь. Ваш код может привести к неожиданным ситуациям в зависимости от конфигурации системы.

Ответ №3:

127.0.0.1 является ли localhost адресом обратной связи для компьютера, на котором вы в данный момент находитесь. Вы, вероятно, захотите изменить это на реальный ip в клиенте, если используете два отдельных поля.

Да, клиент обычно, но не всегда, находится в другом физическом блоке от сервера. Вы можете просто запустить обе части на omne box, если хотите

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

1. Спасибо, сэр .. Итак, я меняю ip-адрес в клиентском коде на ip-адрес серверной машины.. Отлично.. Но что мне делать с ip-адресом в коде сервера, вот, my_addr.sin_addr.s_addr = inet_addr(«127.0.0.1»);

2. Вам никогда не придется тыкать в sin_addr.s_addr и т.д. Вручную. Это плохой вид программирования сокетов начала 90-х, который принесет вам много головной боли и затруднит поддержку IPv6. Смотрите мой ответ о современном способе привязки сокета для прослушивания.

Ответ №4:

1) Допустим, один компьютер является сервером, а другой — клиентом. Теперь, должен ли серверный код находиться на серверном компьютере, а клиентский — на клиентском??

Я не думаю, что понимаю это с правильной точки зрения … lol.Если ваш клиентский код находится на стороне сервера, как вы могли бы выделить какие-либо объемы памяти или вызвать что-либо на клиентском компьютере?

2) В коде сервера, когда мы предоставляем ip-адрес для bind(), это должен быть ip-адрес системы, который мы можем найти через ipconfig, или он все еще должен оставаться адресом обратной связи??

что мы имеем на странице руководства:

  int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  

«bind() присваивает адрес, указанный в addr, сокету, на который ссылается файловый дескриптор sockfd. addrlen определяет размер в байтах адресной структуры, на которую указывает addr. Традиционно эта операция называется «присвоение имени сокету».
Обычно необходимо назначить локальный адрес с помощью bind(), прежде чем сокет SOCK_STREAM сможет получать соединения (см. accept(2)).»

В вашем случае, я полагаю, 127.0.0.1 — это то, что вы ищете.В двух словах, адрес, о котором вы говорили, скорее всего, тот, который вы настроили для структуры server_addr.

3) В клиентском коде, я полагаю, IP-адрес назначения должен быть адресом серверного компьютера, верно??

ДА.

4) И последнее, и самое важное, КАК мне ПОДКЛЮЧИТЬ ДВА КОМПЬЮТЕРА??

Звучит как классическое приложение для чата. Насколько я знаю (я новичок …), программирование UDP (User Datagram Protocol) и API, вероятно, стоит попробовать.Если вы хотите придерживаться TCP / IP, два произвольных компьютера, по сути, не подключаются друг к другу, но оба сохраняют связь с сервером, и сервер будет краеугольным камнем между ними.

Более того, я заглянул на какую-то справочную страницу сокета:

  int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  

«Системный вызов connect() соединяет сокет, на который ссылается файловый дескриптор sockfd, с адресом, указанным addr. Аргумент addrlen определяет размер addr. Формат адреса в addr определяется адресным пространством сокета sockfd; дополнительные подробности см. в разделе сокет(2).»

Я думаю, что это прекрасно объясняет ваш вопрос теоретическим образом.

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

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