Last active
November 24, 2019 23:35
-
-
Save jirihnidek/9c256703a60eb077b6a6 to your computer and use it in GitHub Desktop.
Example of IPv6 TCP client-server application(s) using select() and blocking sockets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <arpa/inet.h> | |
#include <netinet/in.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/socket.h> | |
#include <unistd.h> | |
#define SERVER_PORT 7002 | |
int main(int argc, char *argv[]) | |
{ | |
int sock_fd = -1; | |
struct sockaddr_in6 server_addr; | |
int ret; | |
char ch = 'a'; | |
/* Arguments could be used in getaddrinfo() to get e.g. IP of server */ | |
(void)argc; | |
(void)argv; | |
/* Create socket for communication with server */ | |
sock_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); | |
if (sock_fd == -1) { | |
perror("socket()"); | |
return EXIT_FAILURE; | |
} | |
/* Connect to server running on localhost */ | |
server_addr.sin6_family = AF_INET6; | |
inet_pton(AF_INET6, "::1", &server_addr.sin6_addr); | |
server_addr.sin6_port = htons(SERVER_PORT); | |
/* Try to do TCP handshake with server */ | |
ret = connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); | |
if (ret == -1) { | |
perror("connect()"); | |
close(sock_fd); | |
return EXIT_FAILURE; | |
} | |
printf("Sending %c to server\n", ch); | |
/* Send data to server */ | |
ret = write(sock_fd, &ch, 1); | |
if (ret == -1) { | |
perror("write"); | |
close(sock_fd); | |
return EXIT_FAILURE; | |
} | |
/* Wait for data from server */ | |
ret = read(sock_fd, &ch, 1); | |
if (ret == -1) { | |
perror("read()"); | |
close(sock_fd); | |
return EXIT_FAILURE; | |
} | |
printf("Received %c from server\n", ch); | |
/* Do something 'useful' */ | |
sleep(2); | |
printf("Sending %c to server\n", ch); | |
/* Send data to server again */ | |
ret = write(sock_fd, &ch, 1); | |
if (ret == -1) { | |
perror("write"); | |
close(sock_fd); | |
return EXIT_FAILURE; | |
} | |
/* Wait for data from server again */ | |
ret = read(sock_fd, &ch, 1); | |
if (ret == -1) { | |
perror("read()"); | |
close(sock_fd); | |
return EXIT_FAILURE; | |
} | |
printf("Received %c from server\n", ch); | |
/* DO TCP teardown */ | |
ret = close(sock_fd); | |
if (ret == -1) { | |
perror("close()"); | |
return EXIT_FAILURE; | |
} | |
return EXIT_SUCCESS; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Main CMakeFile.txt | |
# Minimal version of CMake | |
cmake_minimum_required (VERSION 2.6) | |
# Build type | |
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) | |
message(STATUS "Setting build type to 'Debug' as none was specified.") | |
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) | |
# Set the possible values of build type for cmake-gui | |
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") | |
endif () | |
# Define project name | |
project (Client-Server) | |
# Source code for server | |
set (server_src server.c) | |
# Source code for client | |
set (client_src client.c) | |
# Compiler flags | |
if (CMAKE_COMPILER_IS_GNUCC) | |
set (CMAKE_C_FLAGS "-D_REETRANT -Wall -Wextra -pedantic -Wno-long-long") | |
if (CMAKE_BUILD_TYPE STREQUAL "Debug") | |
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0") | |
elseif( CMAKE_BUILD_TYPE STREQUAL "Release" ) | |
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG -O3 -fno-strict-aliasing") | |
endif () | |
endif (CMAKE_COMPILER_IS_GNUCC) | |
# Set up verse server executable | |
add_executable (server ${server_src}) | |
# Set up verse server executable | |
add_executable (client ${client_src}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <arpa/inet.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/socket.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
#include <netinet/in.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#define CLIENT_QUEUE_LEN 10 | |
#define SERVER_PORT 7002 | |
/* This function closes socket used for communication with client */ | |
void close_client_socket(int client_sock_fd, int *max_fd, fd_set *set) | |
{ | |
int ret; | |
printf("Closing connection #%d ...\n", client_sock_fd); | |
ret = close(client_sock_fd); | |
if (ret == -1) { | |
perror("close()"); | |
client_sock_fd = -1; | |
} | |
FD_CLR(client_sock_fd, set); | |
*max_fd = (client_sock_fd == *max_fd) ? client_sock_fd - 1 : *max_fd; | |
} | |
int main(void) | |
{ | |
int listen_sock_fd = -1, client_sock_fd = -1, sock_fd, max_fd = -1; | |
struct sockaddr_in6 server_addr, client_addr; | |
socklen_t client_addr_len; | |
char str_addr[INET6_ADDRSTRLEN]; | |
int ret, flag; | |
fd_set sock_set, work_set; | |
struct timeval timeout; | |
char ch; | |
/* Create socket for listening (client requests) */ | |
listen_sock_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); | |
if(listen_sock_fd == -1) { | |
perror("socket()"); | |
return EXIT_FAILURE; | |
} | |
/* Set socket to reuse address */ | |
flag = 1; | |
ret = setsockopt(listen_sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); | |
if(ret == -1) { | |
perror("setsockopt()"); | |
close(listen_sock_fd); | |
return EXIT_FAILURE; | |
} | |
server_addr.sin6_family = AF_INET6; | |
server_addr.sin6_addr = in6addr_any; | |
server_addr.sin6_port = htons(SERVER_PORT); | |
/* Bind address and socket together */ | |
ret = bind(listen_sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); | |
if(ret == -1) { | |
perror("bind()"); | |
close(listen_sock_fd); | |
return EXIT_FAILURE; | |
} | |
/* Create listening queue (client requests) */ | |
ret = listen(listen_sock_fd, CLIENT_QUEUE_LEN); | |
if (ret == -1) { | |
perror("listen()"); | |
close(listen_sock_fd); | |
return EXIT_FAILURE; | |
} | |
client_addr_len = sizeof(client_addr); | |
printf("Waiting ...\n"); | |
/* Initialize set of file descriptors */ | |
FD_ZERO(&sock_set); | |
/* Add listen socket to the set of sockets */ | |
FD_SET(listen_sock_fd, &sock_set); | |
/* Listen socket is the max socket */ | |
max_fd = listen_sock_fd; | |
while(1) { | |
/* Set up timeout */ | |
timeout.tv_sec = 1; | |
timeout.tv_usec = 0; | |
/* Copy current set of file descriptors to the working set of | |
* file descriptors */ | |
memcpy(&work_set, &sock_set, sizeof(fd_set)); | |
/* Wait one second if something happens */ | |
ret = select(FD_SETSIZE, &work_set, NULL, NULL, &timeout); | |
if (ret > 0) { | |
/* Remember number of events on sockets */ | |
int count = ret; | |
printf("Select ...\n"); | |
/* Iterate over all sockets */ | |
for(sock_fd = 0; sock_fd <= max_fd && count > 0; sock_fd++) { | |
/* Test if event was on socket */ | |
if(FD_ISSET(sock_fd, &work_set)) { | |
count--; | |
/* Was event on main listen socket (new connection)? */ | |
if(sock_fd == listen_sock_fd) { | |
/* Do TCP handshake with client */ | |
client_sock_fd = accept(listen_sock_fd, | |
(struct sockaddr*)&client_addr, | |
&client_addr_len); | |
if (client_sock_fd == -1) { | |
perror("accept()"); | |
close(listen_sock_fd); | |
return EXIT_FAILURE; | |
} | |
inet_ntop(AF_INET6, &(client_addr.sin6_addr), | |
str_addr, sizeof(str_addr)); | |
printf("New connection #%d from: %s:%d ...\n", | |
client_sock_fd, | |
str_addr, | |
ntohs(client_addr.sin6_port)); | |
/* Add client socket to set of socket file descriptors */ | |
FD_SET(client_sock_fd, &sock_set); | |
/* Update the biggest file descriptor */ | |
max_fd = (client_sock_fd > max_fd) ? client_sock_fd : max_fd; | |
} | |
/* When event was not on listen socket, then it had to be on | |
* client socket and some data were received. */ | |
else { | |
int nread; | |
/* Are there any data to read. */ | |
ret = ioctl(sock_fd, FIONREAD, &nread); | |
if (ret == -1) { | |
perror("ioctl()"); | |
close(listen_sock_fd); | |
return EXIT_FAILURE; | |
} | |
/* When there are no data to read, then FIN packet was received | |
* and server should close the connection. */ | |
if(nread == 0) { | |
close_client_socket(sock_fd, &max_fd, &sock_set); | |
} else { | |
/* Wait for data from client */ | |
ret = read(sock_fd, &ch, 1); | |
if (ret == -1) { | |
perror("read()"); | |
close_client_socket(sock_fd, &max_fd, &sock_set); | |
continue; | |
} | |
printf("Received data: %c from #%d\n", ch, sock_fd); | |
/* Do very useful thing with received data :-) */ | |
ch++; | |
/* Send response to client */ | |
ret = write(sock_fd, &ch, 1); | |
if (ret == -1) { | |
perror("write()"); | |
close_client_socket(sock_fd, &max_fd, &sock_set); | |
continue; | |
} | |
} | |
} | |
} | |
} | |
} else if(ret == 0) { | |
printf("Timeout %d ...\n", max_fd); | |
} else { | |
perror("select()"); | |
close(listen_sock_fd); | |
return EXIT_FAILURE; | |
} | |
} | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment