#c #sockets #client
#c #сокеты #клиент
Вопрос:
Я только начал изучать программирование сокетов и нахожу это довольно интересным. В настоящее время я создаю сервер и клиент на одном компьютере, и, следовательно, я могу использовать ip-адрес в качестве адреса обратной связи, 127.0.0.1 и, кажется, все работает нормально!!
Но теперь я думал о том, чтобы иметь два компьютера и делать это.. У меня есть следующие вопросы
- Допустим, один компьютер является сервером, а другой — клиентом. Теперь, должен ли серверный код находиться на серверном компьютере, а клиентский код — на клиентском?
- В коде сервера, когда мы указываем ip-адрес для
bind()
, это должен быть ip-адрес системы, который мы можем найти черезipconfig
, или он все еще должен оставаться адресом обратной связи? - В клиентском коде, я полагаю, IP-адрес назначения должен быть адресом серверного компьютера, верно??
- И последнее, и самое важное, КАК мне ПОДКЛЮЧИТЬ ДВА КОМПЬЮТЕРА??
Я прилагаю простой код передачи сообщений серверу и клиенту, с которого я начал. Будьте добры, расскажите мне об изменениях, которые мне нужно внести..
СЕРВЕРНЫЙ КОД
#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, тогда сервер и клиент находятся на двух отдельных компьютерах, и сервер еще может выделить некоторую память на серверном компьютере для клиента. Я думаю, что это концептуально правильно, пожалуйста, но поправьте меня, если я ошибаюсь