Skip to content

Instantly share code, notes, and snippets.

@kosh04
Last active September 7, 2018 18:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kosh04/3772420 to your computer and use it in GitHub Desktop.
Save kosh04/3772420 to your computer and use it in GitHub Desktop.
WinSock2+FILE I/Oを利用したネットワークサーバのサンプル
#include "server.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define RCVBUFSIZE 16
char *upcase(char *s) {
for (char *p=s; *p; ++p)
*p = toupper(*p);
return s;
}
char *chomp(char *s) {
char *eol = strpbrk(s, "\n");
if (eol) *eol = '\0';
return s;
}
void execute_echo(FILE *r, FILE *w) {
char line[RCVBUFSIZE+1] = {0};
while (fgets(line, sizeof(line), r) != NULL) {
//fprintf(w, "%s", upcase(line));
size_t len = strlen(line);
size_t n = fwrite(upcase(line), 1, len, w);
if (n < len) {
debug("write %"PRIdSIZE" byte, but expect %"PRIdSIZE" byte: '%s\\n'\n", n, len, chomp(line));
}
}
}
int main() {
start_server("1234", execute_echo);
return 0;
}
/**
* http-server.c
*
* Usage:
*
* [server]$ make http-server
* [server]$ ./http-server 4711
* [client]$ curl -v localhost 4711
*/
#include "server.h"
#include <stdio.h>
#include <string.h>
#define RCVBUFSIZE 255
void usage(char *progname)
{
fprintf(stderr, "Usage: %s PORT\n", progname);
}
void execute_http_request(FILE *r, FILE *w)
{
char buf[RCVBUFSIZE + 1] = {0};
// read headers (ignored)
while (fgets(buf, RCVBUFSIZE + 1, r) != NULL) {
if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0) break;
debug("recv: [%s]\r\n", buf);
}
char contents[] = "Hello, world!";
fprintf(w, "HTTP/1.0 200 OK\r\n");
fprintf(w, "Server: simple httpd 0.1\r\n");
fprintf(w, "Content-Type: text/plain\r\n");
fprintf(w, "Content-Length: %"PRIdSIZE"\r\n", strlen(contents));
fprintf(w, "\r\n");
fprintf(w, "%s", contents);
return;
}
int main(int argc, char *argv[])
{
if (argc < 2) {
usage(argv[0]);
return 1;
}
char *service = argv[1];
start_server(service, execute_http_request);
return 0;
}
ifeq ($(OS),Windows_NT)
CC = gcc
endif
TARGET := http-server echo-server
CFLAGS := -std=gnu99 -Wall -Werror -g3 -O0 \
$(if $(findstring MINGW,$(MSYSTEM)),-Wno-pedantic-ms-format)
LDLIBS := $(if $(findstring MINGW,$(MSYSTEM)),-lws2_32)
default: $(TARGET)
http-server: http-server.o server.o
echo-server: echo-server.o server.o
clean:
$(RM) *.o $(TARGET)
// server.c
#include "server.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <io.h> // _open_osfhandle
typedef int socklen_t;
#else // unix
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
typedef int SOCKET;
#define closesocket close
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET ((SOCKET)(~0))
#endif
#define MAX_BACKLOG 5
void socket_startup()
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
WSACleanup();
exit(EXIT_FAILURE);
}
int opt = SO_SYNCHRONOUS_NONALERT;
setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, sizeof(opt));
#endif
}
void socket_cleanup()
{
#ifdef _WIN32
WSACleanup();
#endif
}
static char *sockaddr_info(const struct sockaddr *sa)
{
static char buf[NI_MAXHOST + NI_MAXSERV + 2] = {0}; // "HOST:PORT\0"
char hostname[NI_MAXHOST] = {0};
char servname[NI_MAXSERV] = {0};
size_t addrlen;
int rc;
if (sa->sa_family == AF_INET)
addrlen = sizeof(struct sockaddr_in);
else // AF_INET6
addrlen = sizeof(struct sockaddr_in6);
rc = getnameinfo(sa, addrlen,
hostname, sizeof(hostname),
servname, sizeof(servname),
NI_NUMERICHOST | NI_NUMERICSERV);
if (rc != 0) {
debug("getnameinfo: %s\n", gai_strerror(rc));
return NULL;
}
snprintf(buf, sizeof(buf), "%s:%s", hostname, servname);
return buf;
}
int tcp_listen(const char *nodename, const char *service)
{
struct addrinfo hints;
struct addrinfo *ans = NULL;
int sockfd, err;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo(nodename, service, &hints, &ans);
if (err != 0) {
debug("getaddrinfo: %s\n", gai_strerror(err));
return -1;
}
/* #ifdef _WIN32
* sockfd = WSASocket(ans->ai_family, ans->ai_socktype, ans->ai_protocol, NULL, 0, 0);
* #else */
sockfd = socket(ans->ai_family, ans->ai_socktype, ans->ai_protocol);
//#endif
if (sockfd == -1) {
perror("socket");
return -1;
}
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&yes, sizeof(yes));
if (bind(sockfd, ans->ai_addr, ans->ai_addrlen) == -1) {
perror("bind");
return -1;
}
if (listen(sockfd, MAX_BACKLOG) == -1) {
perror("listen");
return -1;
}
debug("listening %s.\n", sockaddr_info(ans->ai_addr));
freeaddrinfo(ans);
return sockfd;
}
void start_server(char *service, void (*request_handler)(FILE *, FILE *))
{
socket_startup();
int listen_socket = tcp_listen(NULL, service);
if (listen_socket == INVALID_SOCKET) {
return;
}
for (;;) {
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
int accept_socket = accept(listen_socket, (struct sockaddr *)&from, &fromlen);
if (accept_socket == INVALID_SOCKET) {
perror("accept");
return;
}
debug("accept %s.\n", sockaddr_info((struct sockaddr *)&from));
FILE *r, *w;
#ifdef _WIN32
int h = _open_osfhandle(accept_socket, _O_RDONLY|_O_BINARY);
r = _fdopen(h, "rb");
w = _fdopen(h, "wb");
// r = w = _fdopen(_open_osfhandle(accept_socket, _O_RDWR|_O_BINARY), "r+b");
#else // unix
r = w = fdopen(accept_socket, "r+b");
#endif
if (r == NULL || w == NULL) {
perror("fdopen");
return;
}
if (setvbuf(w, NULL, _IONBF, 0) != 0) {
perror("setvbuf");
}
request_handler(r, w);
//shutdown(accept_socket, SD_BOTH);
fclose(r);
fclose(w);
closesocket(accept_socket);
} // end loop
closesocket(listen_socket);
socket_cleanup();
}
#ifndef SERVER_H
#define SERVER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#define PRIdSIZE "Iu"
#define PRIdSSIZE "Id"
#else
#define PRIdSIZE "zu"
#define PRIdSSIZE "zd"
#endif
// for clang x86_64-pc-windows-mscv
#ifdef _MSC_VER
#define snprintf(s,n,fmt,...) \
_snprintf_s(s, n, _TRUNCATE, fmt, __VA_ARGS__)
#define strtok_r(s,d,p) strtok_s(s,d,p)
#endif
#define debug(...) fprintf(stderr, __VA_ARGS__)
void start_server(char *, void (*)(FILE *, FILE *));
#endif /* SERVER_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment