Skip to content

Instantly share code, notes, and snippets.

@jirihnidek
Last active November 24, 2019 23:35
Show Gist options
  • Save jirihnidek/9c256703a60eb077b6a6 to your computer and use it in GitHub Desktop.
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
#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;
}
# 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})
#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