Initial commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
hello_server
|
||||||
|
hello_client
|
||||||
|
chat_server
|
||||||
|
chat_client
|
||||||
|
udp_server
|
||||||
|
udp_client
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 seajee
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
31
Makefile
Normal file
31
Makefile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-Wall -Wextra -ggdb -I.
|
||||||
|
LDFLAGS=-lpthread
|
||||||
|
|
||||||
|
all: hello_server hello_client chat_server chat_client udp_server udp_client
|
||||||
|
|
||||||
|
chat_server: examples/chat_server.c
|
||||||
|
$(CC) $(CFLAGS) -o chat_server examples/chat_server.c $(LDFLAGS)
|
||||||
|
|
||||||
|
chat_client: examples/chat_client.c
|
||||||
|
$(CC) $(CFLAGS) -o chat_client examples/chat_client.c $(LDFLAGS)
|
||||||
|
|
||||||
|
hello_server: examples/hello_server.c
|
||||||
|
$(CC) $(CFLAGS) -o hello_server examples/hello_server.c $(LDFLAGS)
|
||||||
|
|
||||||
|
hello_client: examples/hello_client.c
|
||||||
|
$(CC) $(CFLAGS) -o hello_client examples/hello_client.c $(LDFLAGS)
|
||||||
|
|
||||||
|
udp_server: examples/udp_server.c
|
||||||
|
$(CC) $(CFLAGS) -o udp_server examples/udp_server.c $(LDFLAGS)
|
||||||
|
|
||||||
|
udp_client: examples/udp_client.c
|
||||||
|
$(CC) $(CFLAGS) -o udp_client examples/udp_client.c $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf chat_server
|
||||||
|
rm -rf chat_client
|
||||||
|
rm -rf hello_server
|
||||||
|
rm -rf hello_client
|
||||||
|
rm -rf udp_server
|
||||||
|
rm -rf udp_client
|
||||||
13
README.md
Normal file
13
README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# sock.h
|
||||||
|
|
||||||
|
A simple C socket library wrapper. Supporting TCP and UDP via IPv4 and IPv6.
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
There are many examples in the repository that show how this library
|
||||||
|
simplifies the usage of sockets in C. To build them you can use the Makefile
|
||||||
|
provided.
|
||||||
|
|
||||||
|
```console
|
||||||
|
make
|
||||||
|
```
|
||||||
74
examples/chat_client.c
Normal file
74
examples/chat_client.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#define SOCK_IMPLEMENTATION
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
#define DEFAULT_ADDRESS "127.0.0.1"
|
||||||
|
#define PORT 6969
|
||||||
|
#define BUFFER_CAPACITY 4096
|
||||||
|
|
||||||
|
bool disconnected = false;
|
||||||
|
|
||||||
|
void *listen_thread(void *user_data)
|
||||||
|
{
|
||||||
|
Sock *server = (Sock*) user_data;
|
||||||
|
|
||||||
|
char buffer[BUFFER_CAPACITY];
|
||||||
|
ssize_t received = 0;
|
||||||
|
|
||||||
|
while ((received = sock_recv(server, buffer, sizeof(buffer))) > 0) {
|
||||||
|
printf("%.*s\n", (int)received, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnected = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const char *address = DEFAULT_ADDRESS;
|
||||||
|
if (argc > 1) {
|
||||||
|
address = argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Sock *client = sock_create(SOCK_IPV4, SOCK_TCP);
|
||||||
|
if (client == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Could not create socket\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sock_connect(client, sock_addr(address, PORT))) {
|
||||||
|
fprintf(stderr, "ERROR: Could not connect to server\n");
|
||||||
|
sock_close(client);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t ltid;
|
||||||
|
if (pthread_create(<id, NULL, &listen_thread, client) != 0) {
|
||||||
|
fprintf(stderr, "ERROR: Could not create listening thread\n");
|
||||||
|
sock_close(client);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
pthread_detach(ltid);
|
||||||
|
|
||||||
|
printf("INFO: Connected to the server\n");
|
||||||
|
printf("INFO: Type `disconnect` to disconnect from the server\n");
|
||||||
|
|
||||||
|
char buffer[BUFFER_CAPACITY];
|
||||||
|
while (!disconnected) {
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
fgets(buffer, sizeof(buffer), stdin);
|
||||||
|
if (strcmp(buffer, "disconnect\n") == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size_t len = strlen(buffer)-1;
|
||||||
|
sock_send(client, buffer, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_close(client);
|
||||||
|
printf("INFO: Closed socket\n");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
179
examples/chat_server.c
Normal file
179
examples/chat_server.c
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#define SOCK_IMPLEMENTATION
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
#define PORT 6969
|
||||||
|
#define POOL_CAPACITY 16
|
||||||
|
#define BUFFER_CAPACITY 4096
|
||||||
|
#define USERNAME_CAPACITY 16
|
||||||
|
|
||||||
|
Sock *socket_pool[POOL_CAPACITY];
|
||||||
|
pthread_mutex_t pool_lock;
|
||||||
|
|
||||||
|
bool add_client(Sock *sock)
|
||||||
|
{
|
||||||
|
bool added = false;
|
||||||
|
pthread_mutex_lock(&pool_lock);
|
||||||
|
for (size_t i = 0; i < POOL_CAPACITY; ++i) {
|
||||||
|
if (socket_pool[i] == NULL) {
|
||||||
|
socket_pool[i] = sock;
|
||||||
|
added = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_lock);
|
||||||
|
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove_client(Sock *sock) {
|
||||||
|
bool removed = false;
|
||||||
|
pthread_mutex_lock(&pool_lock);
|
||||||
|
for (size_t i = 0; i < POOL_CAPACITY; ++i) {
|
||||||
|
if (socket_pool[i] == sock) {
|
||||||
|
socket_pool[i] = NULL;
|
||||||
|
removed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_lock);
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void broadcast(const Sock *from, const char *msg, size_t msg_len)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&pool_lock);
|
||||||
|
for (size_t i = 0; i < POOL_CAPACITY; ++i) {
|
||||||
|
Sock *r = socket_pool[i];
|
||||||
|
if (r != NULL && r != from) {
|
||||||
|
sock_send(r, msg, msg_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *handle_client(void *user_data)
|
||||||
|
{
|
||||||
|
Sock *client = (Sock*) user_data;
|
||||||
|
ssize_t received = 0;
|
||||||
|
|
||||||
|
char buffer[BUFFER_CAPACITY];
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
|
char username[USERNAME_CAPACITY];
|
||||||
|
size_t username_length = 0;
|
||||||
|
memset(username, 0, sizeof(username));
|
||||||
|
|
||||||
|
const char *username_prompt = "username:";
|
||||||
|
size_t prompt_length = strlen(username_prompt);
|
||||||
|
memcpy(buffer, "username: ", prompt_length);
|
||||||
|
sock_send(client, buffer, prompt_length);
|
||||||
|
received = sock_recv(client, username, sizeof(username));
|
||||||
|
if (received < 0) {
|
||||||
|
goto disconnect;
|
||||||
|
}
|
||||||
|
username_length = received;
|
||||||
|
|
||||||
|
printf("INFO: Client login with username `%.*s`\n", (int)username_length,
|
||||||
|
username);
|
||||||
|
received = snprintf(buffer, sizeof(buffer), "[Server] `%.*s` joined the chat",
|
||||||
|
(int)username_length, username);
|
||||||
|
broadcast(client, buffer, received);
|
||||||
|
|
||||||
|
memcpy(buffer, username, username_length);
|
||||||
|
buffer[username_length] = ':';
|
||||||
|
buffer[username_length + 1] = ' ';
|
||||||
|
size_t prefix_len = username_length + 2;
|
||||||
|
|
||||||
|
size_t read_len = sizeof(buffer) - prefix_len;
|
||||||
|
char *read_buf = buffer + prefix_len;
|
||||||
|
|
||||||
|
while ((received = sock_recv(client, read_buf, read_len)) > 0) {
|
||||||
|
printf("INFO: %.*s: %.*s\n", (int)username_length, username,
|
||||||
|
(int)received, read_buf);
|
||||||
|
broadcast(client, buffer, received + prefix_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect:
|
||||||
|
sock_close(client);
|
||||||
|
remove_client(client);
|
||||||
|
|
||||||
|
printf("INFO: Client `%.*s` disconnected\n", (int)username_length,
|
||||||
|
username);
|
||||||
|
received = snprintf(buffer, sizeof(buffer), "[Server] `%.*s` left the chat",
|
||||||
|
(int)username_length, username);
|
||||||
|
broadcast(client, buffer, received);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Sock *server = sock_create(SOCK_IPV4, SOCK_TCP);
|
||||||
|
if (server == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Could not create socket\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("INFO: Created socket\n");
|
||||||
|
|
||||||
|
if (!sock_bind(server, sock_addr("0.0.0.0", PORT))) {
|
||||||
|
fprintf(stderr, "ERROR: Could not bind socket\n");
|
||||||
|
sock_close(server);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("INFO: Bind socket\n");
|
||||||
|
|
||||||
|
if (!sock_listen(server, 10)) {
|
||||||
|
fprintf(stderr, "ERROR: Could not listen on socket\n");
|
||||||
|
sock_close(server);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("INFO: Listen socket\n");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
Sock *client = sock_accept(server);
|
||||||
|
if (client == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Could not accept client\n");
|
||||||
|
sock_close(client);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!add_client(client)) {
|
||||||
|
fprintf(stderr, "ERROR: TCP_Socket pool buffer is full (%d)\n",
|
||||||
|
POOL_CAPACITY);
|
||||||
|
sock_close(client);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("INFO: Pool:\n");
|
||||||
|
// pthread_mutex_lock(&pool_lock);
|
||||||
|
// for (size_t i = 0; i < POOL_CAPACITY; ++i) {
|
||||||
|
// printf(" %ld: %p\n", i, socket_pool[i]);
|
||||||
|
// }
|
||||||
|
// pthread_mutex_unlock(&pool_lock);
|
||||||
|
// printf("INFO: End pool:\n");
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
if (pthread_create(&thread, NULL, &handle_client, client) != 0) {
|
||||||
|
fprintf(stderr, "ERROR: Could not create thread\n");
|
||||||
|
sock_close(client);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pthread_detach(thread);
|
||||||
|
|
||||||
|
printf("INFO: New client connected\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_close(server);
|
||||||
|
printf("INFO: Closed socket\n");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
36
examples/hello_client.c
Normal file
36
examples/hello_client.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define SOCK_IMPLEMENTATION
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char *err = "None";
|
||||||
|
|
||||||
|
Sock *sock = sock_create(SOCK_IPV4, SOCK_TCP);
|
||||||
|
if (sock == NULL) { err = "create"; goto defer; }
|
||||||
|
printf("create\n");
|
||||||
|
|
||||||
|
SockAddr addr = sock_addr("127.0.0.1", 6969);
|
||||||
|
|
||||||
|
if (!sock_connect(sock, addr)) { err = "connect"; goto defer; }
|
||||||
|
printf("connect\n");
|
||||||
|
|
||||||
|
const char *msg = "Hello from client!";
|
||||||
|
char buf[128];
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
sock_send(sock, msg, strlen(msg));
|
||||||
|
printf("send\n");
|
||||||
|
sock_recv(sock, buf, sizeof(buf));
|
||||||
|
printf("recv\n");
|
||||||
|
printf("%.*s\n", (int)sizeof(buf), buf);
|
||||||
|
|
||||||
|
defer:
|
||||||
|
sock_close(sock);
|
||||||
|
printf("close\n");
|
||||||
|
fprintf(stderr, "ERROR: %s: %s\n", err, strerror(errno));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
43
examples/hello_server.c
Normal file
43
examples/hello_server.c
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define SOCK_IMPLEMENTATION
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
char *err = "None";
|
||||||
|
|
||||||
|
Sock *sock = sock_create(SOCK_IPV4, SOCK_TCP);
|
||||||
|
if (sock == NULL) { err = "create"; goto defer; }
|
||||||
|
printf("create\n");
|
||||||
|
|
||||||
|
SockAddr addr = sock_addr("0.0.0.0", 6969);
|
||||||
|
if (!sock_bind(sock, addr)) { err = "bind"; goto defer; }
|
||||||
|
printf("bind\n");
|
||||||
|
if (!sock_listen(sock, 16)) { err = "listen"; goto defer; }
|
||||||
|
printf("listen\n");
|
||||||
|
|
||||||
|
Sock *client = sock_accept(sock);
|
||||||
|
if (client == NULL) { err = "accept"; goto defer; }
|
||||||
|
printf("accept\n");
|
||||||
|
|
||||||
|
const char *msg = "Hello from server!";
|
||||||
|
char buf[128];
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
sock_send(client, msg, strlen(msg));
|
||||||
|
printf("send\n");
|
||||||
|
sock_recv(client, buf, sizeof(buf));
|
||||||
|
printf("recv\n");
|
||||||
|
printf("%.*s\n", (int)sizeof(buf), buf);
|
||||||
|
|
||||||
|
sock_close(client);
|
||||||
|
printf("close\n");
|
||||||
|
|
||||||
|
defer:
|
||||||
|
sock_close(sock);
|
||||||
|
printf("close\n");
|
||||||
|
fprintf(stderr, "ERROR: %s: %s\n", err, strerror(errno));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
27
examples/udp_client.c
Normal file
27
examples/udp_client.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define SOCK_IMPLEMENTATION
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Sock *sock = sock_create(SOCK_IPV4, SOCK_UDP);
|
||||||
|
if (sock == NULL) {
|
||||||
|
perror("sock_create");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *msg = "Hello from client!";
|
||||||
|
sock_sendto(sock, msg, strlen(msg), sock_addr("127.0.0.1", 6969));
|
||||||
|
|
||||||
|
SockAddr server_addr;
|
||||||
|
char buf[128];
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
sock_recvfrom(sock, buf, sizeof(buf), &server_addr);
|
||||||
|
|
||||||
|
printf("Received \"%.*s\" from %s:%d\n", (int)sizeof(buf), buf,
|
||||||
|
server_addr.str, server_addr.port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
35
examples/udp_server.c
Normal file
35
examples/udp_server.c
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define SOCK_IMPLEMENTATION
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Sock *server = sock_create(SOCK_IPV4, SOCK_UDP);
|
||||||
|
if (server == NULL) {
|
||||||
|
perror("sock_create");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sock_bind(server, sock_addr("0.0.0.0", 6969))) {
|
||||||
|
perror("sock_bind");
|
||||||
|
sock_close(server);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr client_addr;
|
||||||
|
char buf[128];
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
sock_recvfrom(server, buf, sizeof(buf), &client_addr);
|
||||||
|
|
||||||
|
printf("Received \"%.*s\" from %s:%d\n", (int)sizeof(buf), buf,
|
||||||
|
client_addr.str, client_addr.port);
|
||||||
|
|
||||||
|
const char *msg = "Hello from server!";
|
||||||
|
sock_sendto(server, msg, strlen(msg), client_addr);
|
||||||
|
|
||||||
|
sock_close(server);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
225
sock.h
Normal file
225
sock.h
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
#ifndef SOCK_H_
|
||||||
|
#define SOCK_H_
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define SOCK_ADDR_STR_CAPACITY 64
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SOCK_ADDR_INVALID,
|
||||||
|
SOCK_IPV4,
|
||||||
|
SOCK_IPV6
|
||||||
|
} SockAddrType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SockAddrType type;
|
||||||
|
int port;
|
||||||
|
struct sockaddr sockaddr;
|
||||||
|
socklen_t len;
|
||||||
|
char str[SOCK_ADDR_STR_CAPACITY];
|
||||||
|
} SockAddr;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SOCK_TCP,
|
||||||
|
SOCK_UDP
|
||||||
|
} SockType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SockType type;
|
||||||
|
SockAddr addr;
|
||||||
|
int fd;
|
||||||
|
} Sock;
|
||||||
|
|
||||||
|
SockAddr sock_addr(const char *addr, int port);
|
||||||
|
|
||||||
|
Sock *sock_create(SockAddrType domain, SockType type);
|
||||||
|
bool sock_bind(Sock *sock, SockAddr addr);
|
||||||
|
bool sock_listen(Sock *sock, int backlog);
|
||||||
|
Sock *sock_accept(Sock *sock);
|
||||||
|
bool sock_connect(Sock *sock, SockAddr addr);
|
||||||
|
ssize_t sock_send(Sock *sock, const void *buf, size_t size);
|
||||||
|
ssize_t sock_recv(Sock *sock, void *buf, size_t size);
|
||||||
|
ssize_t sock_sendto(Sock *sock, const void *buf, size_t size, SockAddr addr);
|
||||||
|
ssize_t sock_recvfrom(Sock *sock, void *buf, size_t size, SockAddr *addr);
|
||||||
|
void sock_close(Sock *sock);
|
||||||
|
|
||||||
|
#endif // SOCK_H_
|
||||||
|
|
||||||
|
#ifdef SOCK_IMPLEMENTATION
|
||||||
|
|
||||||
|
SockAddr sock_addr(const char *addr, int port)
|
||||||
|
{
|
||||||
|
SockAddr sa = {0};
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET, addr, &sa.sockaddr) == 1) {
|
||||||
|
sa.type = SOCK_IPV4;
|
||||||
|
sa.len = sizeof(struct sockaddr_in);
|
||||||
|
struct sockaddr_in *in = (struct sockaddr_in*)&sa.sockaddr;
|
||||||
|
in->sin_family = AF_INET;
|
||||||
|
in->sin_port = htons(port);
|
||||||
|
} else if (inet_pton(AF_INET6, addr, &sa.sockaddr) == 1) {
|
||||||
|
sa.type = SOCK_IPV6;
|
||||||
|
sa.len = sizeof(struct sockaddr_in6);
|
||||||
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&sa.sockaddr;
|
||||||
|
in6->sin6_family = AF_INET6;
|
||||||
|
in6->sin6_port = htons(port);
|
||||||
|
} else {
|
||||||
|
sa.type = SOCK_ADDR_INVALID;
|
||||||
|
return sa;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa.port = port;
|
||||||
|
strncpy(sa.str, addr, sizeof(sa.str));
|
||||||
|
|
||||||
|
return sa;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sock *sock_create(SockAddrType domain, SockType type)
|
||||||
|
{
|
||||||
|
Sock *sock = malloc(sizeof(*sock));
|
||||||
|
if (sock == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(sock, 0, sizeof(*sock));
|
||||||
|
|
||||||
|
sock->type = type;
|
||||||
|
|
||||||
|
int sock_domain = 0;
|
||||||
|
int sock_type = 0;
|
||||||
|
|
||||||
|
switch (domain) {
|
||||||
|
case SOCK_IPV4: sock_domain = AF_INET; break;
|
||||||
|
case SOCK_IPV6: sock_domain = AF_INET6; break;
|
||||||
|
default: {
|
||||||
|
free(sock);
|
||||||
|
return NULL;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case SOCK_TCP: sock_type = SOCK_STREAM; break;
|
||||||
|
case SOCK_UDP: sock_type = SOCK_DGRAM; break;
|
||||||
|
default: {
|
||||||
|
free(sock);
|
||||||
|
return NULL;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock->fd = socket(sock_domain, sock_type, 0);
|
||||||
|
if (sock->fd < 0) {
|
||||||
|
free(sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sock_bind(Sock *sock, SockAddr addr)
|
||||||
|
{
|
||||||
|
if (bind(sock->fd, &addr.sockaddr, addr.len) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock->addr = addr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sock_listen(Sock *sock, int backlog)
|
||||||
|
{
|
||||||
|
if (listen(sock->fd, backlog) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sock *sock_accept(Sock *sock)
|
||||||
|
{
|
||||||
|
int fd = accept(sock->fd, &sock->addr.sockaddr, &sock->addr.len);
|
||||||
|
if (fd < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sock *res = malloc(sizeof(*res));
|
||||||
|
if (res == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(res, 0, sizeof(*res));
|
||||||
|
|
||||||
|
res->type = sock->type;
|
||||||
|
res->fd = fd;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sock_connect(Sock *sock, SockAddr addr)
|
||||||
|
{
|
||||||
|
if (connect(sock->fd, &addr.sockaddr, addr.len) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t sock_send(Sock *sock, const void *buf, size_t size)
|
||||||
|
{
|
||||||
|
return send(sock->fd, buf, size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t sock_recv(Sock *sock, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
return recv(sock->fd, buf, size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t sock_sendto(Sock *sock, const void *buf, size_t size, SockAddr addr)
|
||||||
|
{
|
||||||
|
return sendto(sock->fd, buf, size, 0, &addr.sockaddr, addr.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t sock_recvfrom(Sock *sock, void *buf, size_t size, SockAddr *addr)
|
||||||
|
{
|
||||||
|
ssize_t res = recvfrom(sock->fd, buf, size, 0, &addr->sockaddr,
|
||||||
|
&addr->len);
|
||||||
|
|
||||||
|
switch (addr->sockaddr.sa_family) {
|
||||||
|
case AF_INET: {
|
||||||
|
struct sockaddr_in *sa = (struct sockaddr_in*)&addr->sockaddr;
|
||||||
|
addr->type = SOCK_IPV4;
|
||||||
|
addr->port = ntohs(sa->sin_port);
|
||||||
|
addr->len = sizeof(*sa);
|
||||||
|
inet_ntop(AF_INET, &sa->sin_addr, addr->str, sizeof(addr->str));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case AF_INET6: {
|
||||||
|
struct sockaddr_in6 *sa = (struct sockaddr_in6*)&addr->sockaddr;
|
||||||
|
addr->type = SOCK_IPV6;
|
||||||
|
addr->port = ntohs(sa->sin6_port);
|
||||||
|
addr->len = sizeof(*sa);
|
||||||
|
inet_ntop(AF_INET6, &sa->sin6_addr, addr->str, sizeof(addr->str));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
assert(false && "Unsupported address family");
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sock_close(Sock *sock)
|
||||||
|
{
|
||||||
|
close(sock->fd);
|
||||||
|
free(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SOCK_IMPLEMENTATION
|
||||||
Reference in New Issue
Block a user