Сегодня я написал свою первую программу для сокетов. Я основал его на некоторых примерах из справочных страниц и в Интернете.
server.c
:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define ALX_NO_PREFIX
#include <libalx/base/compiler/size.h>
#include <libalx/base/errno/error.h>
#define SERVER_PORT "30002"
#define SERVER_IP "127.0.0.1"
int tcp_server_open (const char *server_port)
{
struct protoent *tcp;
int sd;
struct addrinfo hint = {0};
struct addrinfo *addrs;
int status;
tcp = getprotobyname("tcp");
if (!tcp)
return -EINVAL;
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = tcp->p_proto;
hint.ai_flags = AI_PASSIVE;
status = getaddrinfo(NULL, server_port, &hint, &addrs);
if (status) {
perrorx("getaddrinfo() failed");
return -labs(status);
}
for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) {
sd = socket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol);
if (sd < 0) {
perrorx("socket() failed");
continue;
}
if (!bind(sd, addr->ai_addr, addr->ai_addrlen)) {
perrorx("binded!");
break;
}
close(sd);
sd = -1;
perrorx("bind() failed");
}
freeaddrinfo(addrs);
perrorx("break");
if (sd < 0)
return -errno;
perrorx("open!");
return sd;
}
int main (void)
{
int sd;
char buf[BUFSIZ];
struct sockaddr_storage cli_addr = {0};
socklen_t cli_addr_len;
ssize_t n;
int s;
int status;
char host[NI_MAXHOST];
char service[NI_MAXSERV];
status = EXIT_FAILURE;
sd = tcp_server_open(SERVER_PORT);
if (sd < 0) {
perrorx("tcp_server_open(ROBOT_PORT);");
goto out;
}
while (true) {
cli_addr_len = sizeof(cli_addr);
n = recvfrom(sd, buf, ARRAY_SIZE(buf), 0,
(struct sockaddr *)&cli_addr, &cli_addr_len);
if (n < 0) {
//if (errno != 107)
perrorx("no msg");
continue;
}
s = getnameinfo((struct sockaddr *)&cli_addr, cli_addr_len,
host, NI_MAXHOST, service,
NI_MAXSERV, NI_NUMERICSERV);
if (!s) {
printf("Received %zd bytes from %s:%s\n", n, host, service);
} else {
perrorx("getnameinfo() failed");
fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(s));
}
printf("%*s\n", (int)ARRAY_SSIZE(buf), buf);
if (sendto(sd, buf, n, 0, (struct sockaddr *)&cli_addr, cli_addr_len) != n)
perrorx("sendto() failed");
printf("%s\n", buf);
printf("Enter msg:\n");
fgets(buf, ARRAY_SIZE(buf), stdin);
n = write(sd, buf, strlen(buf));
if (n < 0) {
perrorx("write() failed");
goto out;
}
}
status = EXIT_SUCCESS;
out:
perrorx("out");
return close(sd) || status;
}
client.h
:
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define ALX_NO_PREFIX
#include <libalx/base/compiler/size.h>
#include <libalx/base/errno/error.h>
#define SERVER_PORT "30002"
#define SERVER_IP "127.0.0.1"
int tcp_client_open (const char *server_ip, const char *server_port)
{
struct protoent *tcp;
int sd;
//struct addrinfo hint = {0};
struct addrinfo *addrs;
int status;
tcp = getprotobyname("tcp");
if (!tcp) {
perrorx("getprotobyname() failed");
return -EINVAL;
}
//hint.ai_family = AF_INET;
//hint.ai_socktype = SOCK_STREAM;
//hint.ai_protocol = tcp->p_proto;
status = getaddrinfo(server_ip, server_port, /*&hint*/NULL, &addrs);
if (status) {
perrorx("getaddrinfo() failed");
return -labs(status);
}
for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) {
sd = socket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol);
if (sd < 0) {
perrorx("socket() failed");
break;
}
if (!connect(sd, addr->ai_addr, addr->ai_addrlen)) {
perrorx("connected!");
break;
}
close(sd);
sd = -1;
perrorx("connect() failed");
}
freeaddrinfo(addrs);
perrorx("break");
if (sd < 0)
return -errno;
perrorx("open!");
return sd;
}
int main (void)
{
int sd;
char buf[BUFSIZ];
ssize_t n;
int status;
status = EXIT_FAILURE;
sd = tcp_client_open(SERVER_IP, SERVER_PORT);
if (sd < 0) {
perrorx("tcp_client_open() failed");
goto out;
}
printf("Enter msg:\n");
fgets(buf, ARRAY_SIZE(buf), stdin);
n = write(sd, buf, strlen(buf));
if (n < 0) {
perrorx("write() failed");
goto out;
}
memset(buf, 0, ARRAY_SIZE(buf));
n = read(sd, buf, ARRAY_SIZE(buf));
if (n < 0) {
perrorx("read() failed");
goto out;
}
printf("%*s\n", (int)ARRAY_SSIZE(buf), buf);
status = EXIT_SUCCESS;
out:
perrorx("out");
return close(sd) || status;
}
void perrorx(const char *msg);
обертка вокруг perror, но также печатает имя файла, строки и функции.
Сервер выводит следующее:
./server:
server.c:50:
tcp_server_open():
binded!
E0 - Success
./server:
server.c:59:
tcp_server_open():
break
E0 - Success
./server:
server.c:64:
tcp_server_open():
open!
E0 - Success
, а затем зацикливается в следующем сообщении (если я раскомментирую тест if (errno != 107)
, он ничего не печатает) (попытка подключиться к серверу не влияет на это):
./server:
server.c:97:
main():
no msg
E107 - Transport endpoint is not connected
И я должен убить его с помощью ^ C (конечно, после выхода из клиента ).
Клиент выводит следующее:
./client:
client.c:56:
tcp_client_open():
connect() failed
E111 - Connection refused
./client:
client.c:50:
tcp_client_open():
connected!
E111 - Connection refused
./client:
client.c:59:
tcp_client_open():
break
E111 - Connection refused
./client:
client.c:64:
tcp_client_open():
open!
E111 - Connection refused
Enter msg:
asd
./client:
client.c:95:
main():
read() failed
E111 - Connection refused
./client:
client.c:102:
main():
out
E111 - Connection refused
В чем проблема? Я почти ничего не знаю о сокетах.