Я пытаюсь создать простой клиент-серверный API для двух моих машин. Я сделал эту простую программу, которая использует функции, которые я сделал для ее тестирования. По какой-то причине мой клиент отправляет сообщение нормально, но мой сервер не может (однако он получает сообщение от клиента).
Вывод на стороне сервера:
host name: my_host
Our port number is: 34440
Client msg: Client msg
Send failed: Invalid argument
Message from Server sent to Client
Вывод на стороне клиента:
Connection established with server...
Message from Client sent to Server
Сторона сервера:
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include "my_socket.h"
int main() {
server_init();
char *msg = "Server msg";
char buffer[100];
int n = read_from_client((char *)buffer);
buffer[n] = '\0';
printf("Client msg: %s\n", buffer);
write_to_client((char *)msg);
printf("Message from Server sent to Client \n");
return 0;
}
Сторона клиента:
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include "my_socket.h"
int main() {
client_init();
char *msg = "Client msg";
char buffer[100];
write_to_server((char*)msg);
printf("Message from Client sent to Server \n");
int n = read_from_server((char *)buffer);
buffer[n] = '\0';
printf("Server msg: %s\n", buffer);
close_socket();
return 0;
}
my_socket.c:
#include "my_socket.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
int sockfd1;
int sockfd2;
int MAX_BUFF = 1024;
struct sockaddr_in server;
struct sockaddr_in client;
struct hostent *host;
ssize_t write_to_server(const void *buffer){
int bytes_sent, server_size = sizeof(server), buf_len = strlen(buffer);
if ((bytes_sent = sendto(sockfd2, buffer, buf_len, 0,
(const struct sockaddr *)&server, server_size)) < 0){
perror("Send failed");
}
return bytes_sent;
}
ssize_t write_to_client(const void *buffer){
int bytes_sent, client_size = sizeof(client), buf_len = strlen(buffer);
if ((bytes_sent = sendto(sockfd1, buffer, buf_len, 0,
(const struct sockaddr *)&client, client_size)) < 0){
perror("Send failed");
}
return bytes_sent;
}
int read_from_server(void *buffer){
int bytes_rcv, len;
if ((bytes_rcv = recvfrom(sockfd2, buffer, MAX_BUFF, MSG_WAITALL,
(struct sockaddr *)&server, &len)) < 0){
perror("Read failed");
}
return bytes_rcv;
}
int read_from_client(void *buffer){
int bytes_rcv, len;
if ((bytes_rcv = recvfrom(sockfd1, buffer, MAX_BUFF, MSG_WAITALL,
(struct sockaddr *)&client, &len)) < 0){
perror("Read failed");
}
return bytes_rcv;
}
void close_socket() {
close(sockfd1);
close(sockfd2);
}
void server_init(){
if ( (sockfd1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
}
char name[1024];
name[1023] = '\0';
gethostname(name, 1023);
printf("host name: %s \n", name);
host = gethostbyname("my_host");
if (host == NULL){
perror("Host is null");
exit(0);
}
bzero((char *)&server, sizeof(server));
bzero((char *)&client, sizeof(client));
server.sin_family = AF_INET;
bcopy((char *)host->h_addr,
(char *)&server.sin_addr.s_addr, host->h_length);
//server.sin_port = 0;
server.sin_port = htons(34440);
if ( (bind(sockfd1, (struct sockaddr *)&server, sizeof(server) ) ) < 0 ){
perror("bind failed");
}
socklen_t len = sizeof(server);
if (getsockname(sockfd1, (struct sockaddr *)&server, &len) == -1){
perror("getsockname");
}else{
printf("Our port number is: %d\n", ntohs(server.sin_port));
}
}
void client_init(){
if ( (sockfd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
}
host = gethostbyname("my_host");
if (host == NULL){
perror("Host is null");
exit(0);
}
bzero((char *)&server, sizeof(server));
server.sin_family = AF_INET;
bcopy((char *)host->h_addr,
(char *)&server.sin_addr.s_addr, host->h_length);
//server.sin_port = 0;
server.sin_port = htons(34440);
if (connect(sockfd2, (struct sockaddr *)&server, sizeof(server)) == 0){
printf("Connection established with server...\n");
}
}
my_socket.h:
#ifndef MY_SOCKET
#define MY_SOCKET
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
extern int sockfd1;
extern int sockfd2;
extern int MAX_BUFF;
extern struct sockaddr_in server;
extern struct sockaddr_in client;
extern struct hostent *host;
// Send a message over the socket
ssize_t write_to_server(const void *buffer);
ssize_t write_to_client(const void *buffer);
// Blocks until told it's ready; receives bytes from socket
int read_from_server(void *buffer);
int read_from_client(void *buffer);
// Close the socket
void close_socket();
void server_init();
void client_init();
#endif
Любые советы или критика приветствуются. Заранее спасибо.
Изменения внесены. Спасибо за предупреждение!
По крайней мере, одна проблема заключается в том, что вы не инициализируете переменную len
в read_from_client
. На странице руководства для recvfrom
, в частности, говорится (выделено мной):
...
addrlen
is a value-result argument. Before the call, it should be initialized to the size of the buffer associated withsrc_addr
. Upon return,addrlen
is updated to contain the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case,addrlen
will return a value greater than was supplied to the call.
Это означает, что как неинициализированная переменная стека len
имеет неопределенное значение. Наверное, ноль, но хоть что-то меньшее, чем sizeof(struct sockaddr_in)
. В результате client
не заполняется правильно recvfrom
.
Непосредственно перед recvfrom
вы должны инициализировать его:
len = sizeof(client);
На стороне клиента write_to_server
и read_from_server
не обязательно должны использовать recvfrom
и sendto
, поскольку вы уже сделали connect
на сокете. Они могут просто использовать recv
и send
, поскольку конечная точка удаленного сокета уже установлена connect
. Я считаю, что адрес просто игнорируется для подключенного сокета, но я не могу найти, где это задокументировано прямо сейчас.
(В любом случае, если вы делать продолжите использовать recvfrom
на стороне клиента, вы должны сделать ту же инициализацию len
там.)
Наряду с приведенными ниже ответами, не делайте привычкой игнорировать псевдонимы типов, предоставляемые сетевыми библиотеками и указанные в документации API для каждого вызова API. Они важны. Есть множество мест, где следует использовать
socklen_t
,ssize_t
иsize_t
, где вместо этого вы просто запихиваете все в переменныеint
. Поверьте мне; которые могут укусить вас самыми тонкими и трудными для отладки способами. Следовать правилам.