:(before "End Includes") #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> :(before "End Types") struct socket_t { int fd; sockaddr_in addr; socket_t() { fd = 0; bzero(&addr, sizeof(addr)); } }; :(code) void server_socket(int portno, socket_t* server) { server->fd = socket(AF_INET, SOCK_STREAM, 0); int dummy; setsockopt(server->fd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy)); server->addr.sin_family = AF_INET; server->addr.sin_addr.s_addr = INADDR_ANY; server->addr.sin_port = htons(portno); if (bind(server->fd, (struct sockaddr*)&server->addr, sizeof(server->addr)) < 0) { server->fd = -1; raise << "Failed to bind server socket to port " << portno << ". Something's already using that port." << "\n"; return; } listen(server->fd, 5); } :(before "End Primitive Recipe Declarations") _SOCKET, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$socket", _SOCKET); :(before "End Primitive Recipe Checks") case _SOCKET: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'$socket' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } if (SIZE(inst.products) != 1) { raise << maybe(get(Recipe, r).name) << "'$socket' requires exactly one product, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.products.at(0))) { raise << maybe(get(Recipe, r).name) << "first product of '$socket' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _SOCKET: { int port = ingredients.at(0).at(0); socket_t server; server_socket(port, &server); if (server.fd < 0) { break; } products.resize(1); products.at(0).push_back(server.fd); break; } :(code) void session_socket(int serverfd, socket_t* session) { socklen_t dummy = sizeof(session->addr); session->fd = accept(serverfd, (struct sockaddr*)&session->addr, &dummy); } :(before "End Primitive Recipe Declarations") _ACCEPT, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$accept", _ACCEPT); :(before "End Primitive Recipe Checks") case _ACCEPT: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'$accept' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$accept' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } if (SIZE(inst.products) != 1) { raise << maybe(get(Recipe, r).name) << "'$accept' requires exactly one product, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.products.at(0))) { raise << maybe(get(Recipe, r).name) << "first product of '$accept' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _ACCEPT: { double socket_fd = ingredients.at(0).at(0); socket_t session; session_socket(socket_fd, &session); products.resize(1); products.at(0).push_back(session.fd); break; } :(before "End Primitive Recipe Declarations") _READ_FROM_SOCKET, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$read-from-socket", _READ_FROM_SOCKET); :(before "End Primitive Recipe Checks") case _READ_FROM_SOCKET: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } if (SIZE(inst.products) != 1) { raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one product, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_character(inst.products.at(0))) { raise << maybe(get(Recipe, r).name) << "first product of '$read-from-socket' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _READ_FROM_SOCKET: { products.resize(1); double socket_fd = ingredients.at(0).at(0); char single_char[2]; bzero(single_char, 2); if (read(socket_fd, single_char, 1) < 0) { raise << maybe(current_recipe_name()) << "read from socket failed\n" << end(); products.at(0).push_back(0); break; } products.at(0).push_back(single_char[0]); break; } :(before "End Primitive Recipe Declarations") _CLOSE_SOCKET, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$close-socket", _CLOSE_SOCKET); :(before "End Primitive Recipe Checks") case _CLOSE_SOCKET: { if (SIZE(inst.ingredients) != 2) { raise << maybe(get(Recipe, r).name) << "'$close-socket' requires exactly two ingredient, but got '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-socket' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "t\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _CLOSE_SOCKET: { double socket_fd = ingredients.at(0).at(0); double session_fd = ingredients.at(1).at(0); close(socket_fd); close(session_fd); break; }