Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
947bce1
Add IPv6 support to server
thonda28 Jan 7, 2025
51e3ec6
Modify client to maintain connection after sending data
thonda28 Jan 7, 2025
c7bf378
Handle server disconnection by closing client connection and exiting
thonda28 Jan 7, 2025
bf46092
Implement I/O multiplexing using select()
thonda28 Jan 7, 2025
1835e32
Close socket when max_clients limit is exceeded
thonda28 Jan 7, 2025
cb52c2f
Modify server's main function to accept port as an argument
thonda28 Jan 10, 2025
f5f3534
Extract function declarations to utils.h
thonda28 Jan 10, 2025
36f673c
Modify Client to use parse_port() from utils.h
thonda28 Jan 10, 2025
c0af539
Address review comments
thonda28 Jan 14, 2025
e63117e
Extract client handling logic into handle_client() function
thonda28 Jan 14, 2025
71f16b2
Define BUFFER_SIZE
thonda28 Jan 14, 2025
6d6bd01
Use epoll() instead of select()
thonda28 Jan 14, 2025
1aa9a74
Use stack to manage client sockets efficiently
thonda28 Jan 14, 2025
d726454
Extract new connection handling logic into handle_new_connection() fu…
thonda28 Jan 14, 2025
3b1365d
Declare function signature before main
thonda28 Jan 14, 2025
3554dc5
Add support for both IPv4 and IPv6
thonda28 Jan 14, 2025
435b1d0
Modify client to connect both IPv4 and IPv6 address
thonda28 Jan 15, 2025
43d73ed
Extract socket creation and connection handling logic into create_con…
thonda28 Jan 15, 2025
388a516
Modify parse_port() to return int type
thonda28 Jan 16, 2025
1667b66
Use BUFFER_SIZE instead of sizeof(buf)
thonda28 Jan 16, 2025
a4982b7
Fix some minors
thonda28 Jan 16, 2025
4dc05d0
Modify handle_new_connection() to return int value
thonda28 Jan 16, 2025
f333a30
Modify handle_client() to return int value
thonda28 Jan 16, 2025
fabe42c
Extract socket creation and listening handling logic into create_list…
thonda28 Jan 16, 2025
0504445
Manage client_sockets using SocketManager
thonda28 Jan 16, 2025
94607c9
Refactor to support more than two listen_sockets and manage them usin…
thonda28 Jan 16, 2025
78d925d
Use contains for simplicity
thonda28 Jan 16, 2025
adcb7f9
Remove redundant parts from Makefile
thonda28 Jan 16, 2025
a6865bf
Fix some minors
thonda28 Jan 16, 2025
506020c
Organize error handlings
thonda28 Jan 17, 2025
8d4013a
Add detailed documentation comments for all functions
thonda28 Jan 17, 2025
1e1e22e
Extract listening socket registration logic into add_listen_sockets_t…
thonda28 Jan 17, 2025
442fe0a
Modify server error handling not to exit 1 when error occuring on cli…
thonda28 Jan 17, 2025
03074e4
Refactor SocketManager to dynamically manage the maximum size
thonda28 Jan 17, 2025
634519e
Refactor error handling to use goto for common cleanup
thonda28 Jan 17, 2025
ccf3478
Modify handle_client to execute recv() or send() only once per EPOLLI…
thonda28 Jan 17, 2025
2e313ca
Modify client to ensure complete send and receive of data
thonda28 Jan 17, 2025
1632d0f
Use fwrite() instead of printf()
thonda28 Jan 17, 2025
0232176
Add error handling about EINTR
thonda28 Jan 17, 2025
f7f6b1f
Implement close_with_retry() instead of close() to deal with EINTR
thonda28 Jan 17, 2025
9f70e15
Monitor signal to shutdown after cleanup
thonda28 Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
a.out
client
server
*.o
27 changes: 15 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
.PHONY: build-server
build-server:
gcc -Wall -Wextra -Wpedantic server.c -o server
CC = gcc
CFLAGS = -Wall -Wextra -Wpedantic -O2

.PHONY: run-server
run-server: build-server
./server
.PHONY: all clean

.PHONY: build-client
build-client:
gcc -Wall -Wextra -Wpedantic client.c -o client
all: server client

.PHONY: run-client
run-client:build-client
./client
server: server.o utils.o
$(CC) $(CFLAGS) -o server server.o utils.o
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$@
$^

という変数もあります


client: client.o utils.o
$(CC) $(CFLAGS) -o client client.o utils.o

%.o: %.c
$(CC) $(CFLAGS) -c $<

clean:
rm -f server client *.o
194 changes: 169 additions & 25 deletions client.c
Original file line number Diff line number Diff line change
@@ -1,52 +1,196 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#include "utils.h"

#define BUFFER_SIZE 256

int create_connected_socket(const char *ip, const char *port);

int main(int argc, char **argv)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
// Check if the IP address and port number are provided
if (argc != 3)
{
perror("client: socket()");
printf("Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}

struct sockaddr_in server_addr;
server_addr.sin_family = PF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
// Create a connected socket
int socket_fd;
if ((socket_fd = create_connected_socket(argv[1], argv[2])) == -1)
{
perror("client: connect()");
close(sock);
puts("Failed to create a connected socket\n");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perror は stderr に表示されるので、合わせてこれも stderr に出力した方が良いと思います。

exit(1);
}

char buf[256];
fgets(buf, sizeof(buf), stdin); // Null-terminate the string
while (1)
{
char buf[BUFFER_SIZE];
if (fgets(buf, BUFFER_SIZE, stdin) == NULL)
{
// Check if fgets() reached EOF
if (feof(stdin))
{
puts("EOF detected\n");
break;
}
else
{
perror("client: fgets()");
close_with_retry(socket_fd);
exit(1);
}
}

size_t len = strlen(buf);
if (send(sock, buf, len, 0) == -1)
// Send the input to the server
size_t bytes_read = strlen(buf);
size_t total_sent = 0;
while (total_sent < bytes_read)
{
ssize_t sent_bytes = send(socket_fd, buf + total_sent, bytes_read - total_sent, 0);
if (sent_bytes == -1)
{
if (errno == EINTR)
{
continue;
}
perror("client: send()");
close_with_retry(socket_fd);
exit(1);
}
total_sent += sent_bytes;
}
Comment thread
thonda28 marked this conversation as resolved.

// Receive the response from the server
size_t total_received = 0;
while (total_received < bytes_read)
{
ssize_t received_bytes = recv(socket_fd, buf + total_received, bytes_read - total_received, 0);
if (received_bytes == -1)
{
if (errno == EINTR)
{
continue;
}
perror("client: recv()");
close_with_retry(socket_fd);
exit(1);
}
else if (received_bytes == 0)
{
puts("Connection closed by server\n");
close_with_retry(socket_fd);
exit(1);
}
total_received += received_bytes;
}

// Print the received data
if (fwrite(buf, 1, total_received, stdout) != total_received)
{
perror("client: fwrite()");
close_with_retry(socket_fd);
exit(1);
}
}

close_with_retry(socket_fd);

return 0;
}

/**
* @brief Create a connected socket to the specified IP address and port.
*
* This function creates a socket, connects it to the specified IP address and port,
* and returns the connected socket file descriptor.
*
* @param[in] ip The IP address to connect to.
* @param[in] port The port number to connect to.
* @return The file descriptor of the connected socket.
* @retval -1 An error occurred during the process.
*/
int create_connected_socket(const char *ip, const char *port_str)
{
// Parse the IP address
struct sockaddr_in server_addr4;
struct sockaddr_in6 server_addr6;
memset(&server_addr4, 0, sizeof(server_addr4));
memset(&server_addr6, 0, sizeof(server_addr6));
int is_ipv4 = inet_pton(PF_INET, ip, &server_addr4.sin_addr);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_xxxxx という名前は true or false を連想させるので違う名前にしたい。 (そのままif 文で評価して -1 を true 扱いしてしまう事故が発生しそう)

int is_ipv6 = inet_pton(PF_INET6, ip, &server_addr6.sin6_addr);
if (is_ipv4 <= 0 && is_ipv6 <= 0)
{
perror("client: send()");
close(sock);
exit(1);
printf("Invalid IP address: %s\n", ip);
return -1;
}

if (recv(sock, buf, sizeof(buf) - 1, 0) == -1)
// Parse the port number
int port;
if ((port = parse_port(port_str)) == -1)
{
perror("client: recv()");
close(sock);
exit(1);
printf("Invalid port number: %s\n", port_str);
return -1;
}

printf("%s", buf);
// Create a socket and connect to the server
int socket_fd;
if (is_ipv4 == 1)
{
if ((socket_fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

パラメータが違う同じコードが2か所(v4/v6)にあるので、共通化したい。

{
perror("client: socket()");
return -1;
}

close(sock);
server_addr4.sin_family = PF_INET;
server_addr4.sin_port = htons(port);

return 0;
while (connect(socket_fd, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) == -1)
{
if (errno == EINTR)
{
continue;
}
perror("client: connect() using IPv4");
close_with_retry(socket_fd);
return -1;
}
}
else if (is_ipv6 == 1)
{
if ((socket_fd = socket(PF_INET6, SOCK_STREAM, 0)) == -1)
{
perror("client: socket() using IPv6");
return -1;
}

server_addr6.sin6_family = PF_INET6;
server_addr6.sin6_port = htons(port);

while (connect(socket_fd, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) == -1)
{
if (errno == EINTR)
{
continue;
}
perror("client: connect() using IPv6");
close_with_retry(socket_fd);
return -1;
}
}
else
{
puts("Reached the unreachable");
return -1;
}

printf("Connected to %s, %d\n", ip, port);
return socket_fd;
}
Loading