Skip to content

Instantly share code, notes, and snippets.

@Gelio
Last active April 3, 2017 20:03
Show Gist options
  • Save Gelio/ea237651786e4bc609fde7e94a69b058 to your computer and use it in GitHub Desktop.
Save Gelio/ea237651786e4bc609fde7e94a69b058 to your computer and use it in GitHub Desktop.
Socker communicator with server-client architecture created in C
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <sys/un.h>
#include <pthread.h>
#include <sys/time.h>
#include <stdint.h>
#include <netinet/in.h>
#define SOCKET_UNIX_PATH "/tmp/grzen-comm"
#define SOCKET_UNIX_PATH_SIZE 108
#define USERNAME_SIZE 30
#define BUFFER_SIZE 100
#define ERR(source) (fprintf(stderr, "%s:%d\n", __FILE__, __LINE__), \
perror(source), exit(EXIT_FAILURE) )
void usage(char *filename)
{
fprintf(stderr, "Usage: %s username\n", filename);
fprintf(stderr, "Username must be between 1 and %d characters\n", USERNAME_SIZE - 1);
exit(EXIT_FAILURE);
}
void *printMessages(void *socketDesPtr)
{
int socketDes = *(int*)socketDesPtr;
fd_set baseFds;
FD_ZERO(&baseFds);
FD_SET(socketDes, &baseFds);
fd_set readFds;
while (1)
{
readFds = baseFds;
if (select(socketDes + 1, &readFds, NULL, NULL, NULL) < 0)
ERR("select");
if (!FD_ISSET(socketDes, &readFds))
continue;
uint32_t messageLength;
ssize_t bytesRead = read(socketDes, (char*)&messageLength, sizeof(uint32_t));
if (bytesRead < 0)
ERR("read message length");
else if (bytesRead == 0)
{
printf("Disconnected from the server\n");
break;
}
messageLength = ntohl(messageLength);
char sender[USERNAME_SIZE];
if (read(socketDes, sender, USERNAME_SIZE) < 0)
ERR("read sender name");
char *message = malloc(messageLength * sizeof(char));
if (message == NULL)
ERR("malloc");
if (read(socketDes, message, messageLength) < 0)
ERR("read mesage");
printf("Got a message from %s: %s\n", sender, message);
free(message);
}
return NULL;
}
void login(int socketDes, char *userName)
{
if (write(socketDes, userName, USERNAME_SIZE) < 0)
ERR("write");
printf("Logged in as %s\n", userName);
}
void readConsoleAndSendToServer(int socketDes)
{
while (1)
{
printf("Recipient: ");
char recipient[USERNAME_SIZE];
if (fgets(recipient, USERNAME_SIZE, stdin) <= 0)
ERR("scanf");
recipient[strlen(recipient) - 1] = '\0'; // remove newline
printf("Message: ");
char buffer[BUFFER_SIZE];
if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
ERR("fgets");
buffer[strlen(buffer) - 1] = '\0'; // remove newline
if (strlen(buffer) == 0)
{
printf("Empty message, discarding\n");
continue;
}
printf("Sending message...\n");
uint32_t messageLength = strlen(buffer);
messageLength = htonl(messageLength);
if (write(socketDes, (char*)&messageLength, sizeof(uint32_t)) < 0)
ERR("write message length");
if (write(socketDes, recipient, USERNAME_SIZE) < 0)
ERR("write username");
if (write(socketDes, buffer, strlen(buffer)) < 0)
ERR("write message");
printf("Message sent\n");
}
}
int createUnixSocket()
{
int socketDes = socket(AF_LOCAL, SOCK_STREAM, 0);
if (socketDes < 0)
ERR("socket");
struct sockaddr_un unixAddress;
memset(&unixAddress, 0, sizeof(struct sockaddr_un));
unixAddress.sun_family = AF_LOCAL;
strncpy(unixAddress.sun_path, SOCKET_UNIX_PATH, SOCKET_UNIX_PATH_SIZE);
if (connect(socketDes, (struct sockaddr*)&unixAddress, sizeof(struct sockaddr_un)) < 0)
ERR("connect");
return socketDes;
}
pthread_t createListeningThread(int *socketDes)
{
pthread_t tid;
if (pthread_create(&tid, NULL, printMessages, socketDes) < 0)
ERR("pthread_create");
return tid;
}
void cleanup(pthread_t tid, int socketDes)
{
printf("Joining thread\n");
if (pthread_join(tid, NULL) < 0)
ERR("pthread_join");
printf("Closing socket\n");
if (close(socketDes) < 0)
ERR("socketDes");
printf("Exiting\n");
}
void parseArguments(int argc, char **argv, char *userName)
{
if (argc != 2)
usage(argv[0]);
strncpy(userName, argv[1], USERNAME_SIZE);
int userNameLength = strlen(userName);
if (userNameLength == 0)
usage(argv[0]);
}
int main(int argc, char **argv)
{
char userName[USERNAME_SIZE];
parseArguments(argc, argv, userName);
int socketDes = createUnixSocket();
pthread_t tid = createListeningThread(&socketDes);
login(socketDes, userName);
readConsoleAndSendToServer(socketDes);
cleanup(tid, socketDes);
return EXIT_SUCCESS;
}
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <sys/un.h>
#include <pthread.h>
#include <sys/time.h>
#include <stdint.h>
#include <netinet/in.h>
#include <netdb.h>
#define SOCKET_UNIX_PATH "/tmp/grzen-comm"
#define SOCKET_UNIX_PATH_SIZE 108
#define USERNAME_SIZE 30
#define BUFFER_SIZE 100
#define TCP_PORT 7070
#define ERR(source) (fprintf(stderr, "%s:%d\n", __FILE__, __LINE__), \
perror(source), exit(EXIT_FAILURE) )
void usage(char *filename)
{
fprintf(stderr, "Usage: %s ip username\n", filename);
fprintf(stderr, "Username must be between 1 and %d characters\n", USERNAME_SIZE - 1);
exit(EXIT_FAILURE);
}
void *printMessages(void *socketDesPtr)
{
int socketDes = *(int*)socketDesPtr;
fd_set baseFds;
FD_ZERO(&baseFds);
FD_SET(socketDes, &baseFds);
fd_set readFds;
while (1)
{
readFds = baseFds;
if (select(socketDes + 1, &readFds, NULL, NULL, NULL) < 0)
ERR("select");
if (!FD_ISSET(socketDes, &readFds))
continue;
uint32_t messageLength;
ssize_t bytesRead = read(socketDes, (char*)&messageLength, sizeof(uint32_t));
if (bytesRead < 0)
ERR("read message length");
else if (bytesRead == 0)
{
printf("Disconnected from the server\n");
break;
}
messageLength = ntohl(messageLength);
char sender[USERNAME_SIZE];
if (read(socketDes, sender, USERNAME_SIZE) < 0)
ERR("read sender name");
char *message = malloc(messageLength * sizeof(char));
if (message == NULL)
ERR("malloc");
if (read(socketDes, message, messageLength) < 0)
ERR("read mesage");
printf("Got a message from %s: %s\n", sender, message);
free(message);
}
return NULL;
}
void login(int socketDes, char *userName)
{
if (write(socketDes, userName, USERNAME_SIZE) < 0)
ERR("write");
printf("Logged in as %s\n", userName);
}
void readConsoleAndSendToServer(int socketDes)
{
while (1)
{
printf("Recipient: ");
char recipient[USERNAME_SIZE];
if (fgets(recipient, USERNAME_SIZE, stdin) <= 0)
ERR("scanf");
recipient[strlen(recipient) - 1] = '\0'; // remove newline
printf("Message: ");
char buffer[BUFFER_SIZE];
if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
ERR("fgets");
buffer[strlen(buffer) - 1] = '\0'; // remove newline
if (strlen(buffer) == 0)
{
printf("Empty message, discarding\n");
continue;
}
printf("Sending message...\n");
uint32_t messageLength = strlen(buffer);
messageLength = htonl(messageLength);
if (write(socketDes, (char*)&messageLength, sizeof(uint32_t)) < 0)
ERR("write message length");
if (write(socketDes, recipient, USERNAME_SIZE) < 0)
ERR("write username");
if (write(socketDes, buffer, strlen(buffer)) < 0)
ERR("write message");
printf("Message sent\n");
}
}
int createTCPSocket(char *address)
{
int socketDes = socket(AF_INET, SOCK_STREAM, 0);
if (socketDes < 0)
ERR("socket");
struct sockaddr_in tcpAddress;
struct addrinfo *result;
struct addrinfo hints = {};
hints.ai_family = AF_INET;
int ret;
char port[10];
snprintf(port, 10, "%d", TCP_PORT);
if ((ret = getaddrinfo(address, port, &hints, &result)))
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
exit(EXIT_FAILURE);
}
tcpAddress = *(struct sockaddr_in*)(result->ai_addr);
freeaddrinfo(result);
if (connect(socketDes, (struct sockaddr*)&tcpAddress, sizeof(struct sockaddr_in)) < 0)
ERR("connect");
return socketDes;
}
pthread_t createListeningThread(int *socketDes)
{
pthread_t tid;
if (pthread_create(&tid, NULL, printMessages, socketDes) < 0)
ERR("pthread_create");
return tid;
}
void cleanup(pthread_t tid, int socketDes)
{
printf("Joining thread\n");
if (pthread_join(tid, NULL) < 0)
ERR("pthread_join");
printf("Closing socket\n");
if (close(socketDes) < 0)
ERR("socketDes");
printf("Exiting\n");
}
void parseArguments(int argc, char **argv, char *userName, char **address)
{
if (argc != 3)
usage(argv[0]);
strncpy(userName, argv[2], USERNAME_SIZE);
int userNameLength = strlen(userName);
if (userNameLength == 0)
usage(argv[0]);
*address = argv[1];
}
int main(int argc, char **argv)
{
char userName[USERNAME_SIZE];
char *address;
parseArguments(argc, argv, userName, &address);
int socketDes = createTCPSocket(address);
pthread_t tid = createListeningThread(&socketDes);
login(socketDes, userName);
readConsoleAndSendToServer(socketDes);
cleanup(tid, socketDes);
return EXIT_SUCCESS;
}
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <sys/un.h>
#include <pthread.h>
#include <sys/time.h>
#include <stdint.h>
#include <netinet/in.h>
#include <signal.h>
#define SOCKET_UNIX_PATH "/tmp/grzen-comm"
#define SOCKET_UNIX_PATH_SIZE 108
#define SOCKET_BACKLOG 10
#define MAX_USERS 30
#define SOCKET_POLL_TIMEOUT 2
#define USERNAME_SIZE 30
#define TCP_PORT 7070
typedef struct user {
int active;
int fd;
char name[USERNAME_SIZE];
} user_t;
typedef struct serverData {
user_t *users;
int socketDes;
} serverData_t;
#define ERR(source) (fprintf(stderr, "%s:%d\n", __FILE__, __LINE__), \
perror(source), exit(EXIT_FAILURE) )
void removeLocalSocket(char *path)
{
if (unlink(path) < 0 && errno != ENOENT)
ERR("unlink");
}
int prepareUsersFds(int socketDes, user_t *users, fd_set *fdSet)
{
int maxFd = socketDes;
FD_ZERO(fdSet);
for (int i=0; i < MAX_USERS; i++)
{
if (users[i].active == 0)
continue;
FD_SET(users[i].fd, fdSet);
if (users[i].fd > maxFd)
maxFd = users[i].fd;
}
return maxFd;
}
int getFreeUserId(user_t *users)
{
for (int i=0; i < MAX_USERS; i++)
{
if (users[i].active == 0)
return i;
}
return -1;
}
void sendMessageToUser(user_t *users, char *recipientUserName, char *senderUserName, uint32_t messageSize, char *message)
{
int recipientId = -1;
for (int j=0; j < MAX_USERS; j++)
{
if (!users[j].active)
continue;
if (strncmp(recipientUserName, users[j].name, USERNAME_SIZE) == 0)
{
recipientId = j;
break;
}
}
if (recipientId == -1)
{
printf("Recipient not found\n");
return;
}
uint32_t messageSizeNet = htonl(messageSize);
if (write(users[recipientId].fd, (char*)&messageSizeNet, sizeof(uint32_t)) < 0)
ERR("write message size");
if (write(users[recipientId].fd, senderUserName, USERNAME_SIZE) < 0)
ERR("write username");
if (write(users[recipientId].fd, message, messageSize) < 0)
ERR("write message");
printf("Message transferred\n");
}
void *transferMessages(void *serverDataPtr)
{
serverData_t *srvData = (serverData_t*)serverDataPtr;
int socketDes = srvData->socketDes;
user_t *users = srvData->users;
printf("Thread started\n");
fd_set baseFds;
int maxFd;
struct timeval baseTimeout;
baseTimeout.tv_sec = SOCKET_POLL_TIMEOUT;
baseTimeout.tv_usec = 0;
while (1)
{
struct timeval timeout = baseTimeout;
maxFd = prepareUsersFds(socketDes, users, &baseFds);
if (select(maxFd + 1, &baseFds, NULL, NULL, &timeout) < 0)
ERR("select");
if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
continue;
for (int i=0; i < MAX_USERS; i++)
{
if (!users[i].active)
continue;
int fd = users[i].fd;
if (!FD_ISSET(fd, &baseFds))
continue;
uint32_t messageSize;
ssize_t bytesRead = read(fd, (char*)&messageSize, sizeof(uint32_t));
if (bytesRead < 0)
ERR("read");
else if (bytesRead == 0)
{
printf("Client %s disconnected\n", users[i].name);
if (close(users[i].fd) < 0)
ERR("close");
users[i].active = 0;
continue;
}
messageSize = ntohl(messageSize);
char recipient[USERNAME_SIZE];
if (read(fd, recipient, USERNAME_SIZE) < 0)
ERR("read");
printf("Message of length %d from %s to %s\n", messageSize, users[i].name, recipient);
char *message = malloc(messageSize * sizeof(char));
if (message == NULL)
ERR("malloc");
if (read(fd, message, messageSize) < 0)
ERR("read");
printf("Message: %s\n", message);
sendMessageToUser(users, recipient, users[i].name, messageSize, message);
free(message);
}
}
printf("Thread exits\n");
return NULL;
}
int createUnixSocket()
{
int socketDes = socket(AF_LOCAL, SOCK_STREAM, 0);
if (socketDes < 0)
ERR("socket");
printf("Socket created\n");
return socketDes;
}
void bindUnixSocket(int socketDes)
{
struct sockaddr_un unixAddress;
memset(&unixAddress, 0, sizeof(struct sockaddr_un));
unixAddress.sun_family = AF_LOCAL;
strncpy(unixAddress.sun_path, SOCKET_UNIX_PATH, SOCKET_UNIX_PATH_SIZE);
if (bind(socketDes, (struct sockaddr*)&unixAddress, sizeof(struct sockaddr_un)) < 0)
ERR("bind");
if (listen(socketDes, SOCKET_BACKLOG) < 0)
ERR("listen");
printf("Socket bound\n");
}
pthread_t startMessageRelayThread(serverData_t *srvData)
{
pthread_t tid;
if (pthread_create(&tid, NULL, transferMessages, (void*)srvData) < 0)
ERR("pthread_create");
return tid;
}
void cleanup(user_t *users, int socketDes)
{
for (int i=0; i < MAX_USERS; i++)
{
if (users[i].active == 0)
continue;
if (close(users[i].fd) < 0)
ERR("close");
}
if (close(socketDes) < 0)
ERR("close");
removeLocalSocket(SOCKET_UNIX_PATH);
}
void acceptNewUser(int socketDes, user_t *users)
{
int fd = accept(socketDes, NULL, NULL);
if (fd < 0)
ERR("accept");
int userId = getFreeUserId(users);
if (userId == -1)
{
printf("Server full, rejecting new user\n");
if (close(userId) < 0)
ERR("close");
return;
}
printf("User connected, waiting for username\n");
users[userId].fd = fd;
char username[USERNAME_SIZE];
if (read(fd, username, USERNAME_SIZE) < 0)
ERR("read");
printf("User %s connected and added to the list\n", username);
strncpy(users[userId].name, username, USERNAME_SIZE);
users[userId].active = 1;
}
void *acceptNewUsersTCP(void *srvDataPtr)
{
serverData_t *srvData = (serverData_t*)srvDataPtr;
int socketDes = srvData->socketDes;
user_t *users = srvData->users;
while (1)
acceptNewUser(socketDes, users);
return NULL;
}
int createTCPSocket()
{
int socketDes = socket(AF_INET, SOCK_STREAM, 0);
if (socket < 0)
ERR("socket");
return socketDes;
}
void bindTCPSocket(int socketDes)
{
struct sockaddr_in tcpAddr;
tcpAddr.sin_family = AF_INET;
tcpAddr.sin_port = htons(TCP_PORT);
tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
int t = 1;
if (setsockopt(socketDes, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(t)) < 0)
ERR("setsockopt");
if (bind(socketDes, (struct sockaddr*)&tcpAddr, sizeof(struct sockaddr_in)) < 0)
ERR("bind");
if (listen(socketDes, SOCKET_BACKLOG) < 0)
ERR("listen");
}
int main(int argc, char **argv)
{
removeLocalSocket(SOCKET_UNIX_PATH);
int socketDes = createUnixSocket();
bindUnixSocket(socketDes);
int socketDesTCP = createTCPSocket();
bindTCPSocket(socketDesTCP);
user_t users[MAX_USERS];
for (int i=0; i < MAX_USERS; i++)
users[i].active = 0;
serverData_t srvDataUnix;
srvDataUnix.users = users;
srvDataUnix.socketDes = socketDes;
serverData_t srvDataTCP;
srvDataTCP.users = users;
srvDataTCP.socketDes = socketDesTCP;
printf("Starting message relay thread\n");
pthread_t tid = startMessageRelayThread(&srvDataUnix);
pthread_t tcpThread;
if (pthread_create(&tcpThread, NULL, acceptNewUsersTCP, &srvDataTCP) < 0)
ERR("pthread_create");
printf("Accepting new users\n");
while(1)
acceptNewUser(socketDes, users);
printf("Killing the transferring thread\n");
if (pthread_kill(tid, SIGINT) != 0)
ERR("pthread_kill");
printf("Joining the thread\n");
if (pthread_join(tid, NULL) < 0)
ERR("pthread_join");
printf("Cleanup\n");
cleanup(users, socketDes);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment