amiga-bsdsocket-coding/server_in_c/server.c

209 lines
4.6 KiB
C

/**
* Simple bsdsocket.library server for AmigaOS
* Copyright 2022 John Bintz
* Released under the MIT License
* Visit theindustriousrabbit.com for more fun!
*
* You would not believe how sparse the documentation for
* bsdsocket.library is on the Amiga. All this required
* digging through documentation on multiple sites, in
* multiple forum threads to describe the Amiga quirks
* (Amiga code has quirks? who knew!), and required a bunch of
* experimentation and resetting the Minimig MiSTer core
* and PPP connection to get this working. I tested it
* with CodeWatcher and nothing is leaking, so this should
* be a (hopefully) good bsdsocket example in C for the Amiga.
*
* This code is built for SAS/C. Be sure to disable ChkAbort
* in the Linker options, otherwise the SAS/C Ctrl-c
* handler will take over and screw up server teardown.
*
* You can use netcat/ncat on another machine to send data
* to the running server:
*
* echo "whoa computers" | netcat 192.168.50.100 8090
*
*/
#include <stdio.h>
#include <strings.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <proto/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
// socket stuff
struct Library *SocketBase;
int serverSocket = -1;
struct sockaddr_in serverAddress;
#define PORT (8090)
#define MAX_CONNECTIONS (2)
// data handling
#define BUFFER_LENGTH (256)
char buffer [BUFFER_LENGTH];
int msgSize;
// set up all the global stuff we need, allowing for early exits
// on failures.
int setup(void) {
const int enable = 1;
// this works with the most modern MiamiDX
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) {
printf("Can't open socket library\n");
return 1;
}
serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket < 0) {
printf("can't get a socket\n");
return 1;
}
// AmiTCP 4.3 SDK does not have SO_REUSEPORT, this is all you get
if (setsockopt(
serverSocket,
SOL_SOCKET,
SO_REUSEADDR,
// oh c...
&enable,
sizeof(int)
) < 0) {
printf("can't set socket options\n");
return 1;
}
return 0;
}
void teardown(void) {
if (serverSocket != -1) {
shutdown(
serverSocket,
// SHUT_RDWR, stop all communication both ways
2
);
CloseSocket(serverSocket);
}
if (SocketBase) {
CloseLibrary(SocketBase);
}
}
int startServer(void) {
// bind to all addresses on our defined port
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(PORT);
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(
serverSocket,
(struct sockaddr *) &serverAddress,
sizeof(serverAddress)
) < 0) {
printf("can't bind\n");
return 1;
}
if (listen(serverSocket, MAX_CONNECTIONS) < 0) {
printf("can't listen\n");
return 1;
}
return 0;
}
void readData(int clientSocket) {
do {
// keep stomping over the same buffer
// ideally we'd take the data out of the buffer and
// do something with it.
msgSize = recv(clientSocket, buffer, BUFFER_LENGTH, 0);
if (msgSize <= 0) {
printf("End of data\n");
} else {
printf("Got stuff: %i\n", msgSize);
printf("%s\n", buffer);
}
} while (msgSize == 256);
printf("Data received: %s\n", buffer);
}
int main(void) {
// The incoming netcat client
int clientSocket;
struct sockaddr_in clientAddress;
LONG clientLength = sizeof(clientAddress);
// be able to use WaitSelect
int waitSelectResult;
fd_set readFDs;
if (setup() != 0) {
teardown();
return 1;
}
printf("got socket number %d\n", serverSocket);
if (startServer() != 0) {
teardown();
return 1;
}
printf("listening on port %d\n", PORT);
// just enough to get WaitSelect working
// this macro also wants strings.h included
FD_ZERO(&readFDs);
FD_SET(serverSocket, &readFDs);
waitSelectResult = WaitSelect(
serverSocket + 1,
&readFDs,
NULL, NULL, NULL,
// other Exec Signals would go here as ORed mp_SigBits
NULL
);
// WaitSelect will trap on Ctrl-C for us, and returns -1 if that happens
if (waitSelectResult == -1) {
printf("ctrl-c\n");
teardown();
return 1;
}
if (
(
clientSocket = accept(
serverSocket,
(struct sockaddr *) &clientAddress,
&clientLength
)
) < 0
) {
printf("can't accept\n");
return 1;
}
// note the capitalization difference on Inet_NtoA. the Amiga does
// that a lot...
printf("connection from %s\n", Inet_NtoA(clientAddress.sin_addr.s_addr));
readData(clientSocket);
printf("all done\n");
teardown();
return 0;
}