Changeset View
Changeset View
Standalone View
Standalone View
tools/tools/mq-testing/tcp/server.c
- This file was added.
/*- | |||||
* Copyright (c) 2015, Tiwei Bie <btw@FreeBSD.org> | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice unmodified, this list of conditions, and the following | |||||
* disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <unistd.h> | |||||
#include <ctype.h> | |||||
#include <err.h> | |||||
#include <sys/types.h> | |||||
#include <sys/socket.h> | |||||
#include <sys/event.h> | |||||
#include <netinet/in.h> | |||||
#include <arpa/inet.h> | |||||
#include "common.h" | |||||
static int verbose = 0; | |||||
#define NR_EVENTS 10 | |||||
static void | |||||
usage(const char *prgname) | |||||
{ | |||||
fprintf(stderr, "Usage: %s [-hv]\n", prgname); | |||||
exit(EXIT_FAILURE); | |||||
} | |||||
static char * | |||||
addr2str(int af, void *addr) | |||||
{ | |||||
static char msg[1024]; | |||||
char addrstr[INET6_ADDRSTRLEN]; | |||||
switch (af) { | |||||
case AF_INET: | |||||
snprintf(msg, sizeof(msg), "%s:%d", | |||||
inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), | |||||
ntohs(((struct sockaddr_in *)addr)->sin_port)); | |||||
break; | |||||
case AF_INET6: | |||||
snprintf(msg, sizeof(msg), "%s:%d", | |||||
inet_ntop(AF_INET6, | |||||
&((struct sockaddr_in6 *)addr)->sin6_addr, | |||||
addrstr, sizeof(addrstr)), | |||||
ntohs(((struct sockaddr_in6 *)addr)->sin6_port)); | |||||
break; | |||||
} | |||||
return (msg); | |||||
} | |||||
static int | |||||
kq_add(int kq, int fd) | |||||
{ | |||||
struct kevent changes[1]; | |||||
int ret; | |||||
EV_SET(&changes[0], fd, EVFILT_READ, EV_ADD, 0, 0, NULL); | |||||
ret = kevent(kq, changes, 1, NULL, 0, NULL); | |||||
return (ret); | |||||
} | |||||
static int | |||||
kq_delete(int kq, int fd) | |||||
{ | |||||
struct kevent changes[1]; | |||||
int ret; | |||||
EV_SET(&changes[0], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); | |||||
ret = kevent(kq, changes, 1, NULL, 0, NULL); | |||||
return (ret); | |||||
} | |||||
static void | |||||
accept_new_connection(int kq, int fd, int n) | |||||
{ | |||||
struct sockaddr_in from; | |||||
socklen_t length; | |||||
int newfd, i; | |||||
for (i = 0; i < n; i++) { | |||||
length = sizeof(from); | |||||
newfd = accept(fd, (struct sockaddr *)&from, &length); | |||||
if (newfd < 0) | |||||
err(EXIT_FAILURE, "accept"); | |||||
if (kq_add(kq, newfd) < 0) | |||||
err(EXIT_FAILURE, "kq_add"); | |||||
if (verbose) | |||||
printf("accept connection from %s\n", | |||||
addr2str(AF_INET, &from)); | |||||
} | |||||
} | |||||
static void | |||||
receive_data(int kq, int fd, int n) | |||||
{ | |||||
char msg[BUFSIZ]; | |||||
int i; | |||||
n = read(fd, msg, sizeof(msg)); | |||||
if (n < 0) | |||||
err(EXIT_FAILURE, "read"); | |||||
if (n == 0) { | |||||
if (kq_delete(kq, fd) < 0) | |||||
err(EXIT_FAILURE, "kq_delete"); | |||||
return; | |||||
} | |||||
if (verbose) { | |||||
printf("len=%d ", n); | |||||
for (i = 0; i < n && msg[i]; i++) { | |||||
if (isprint(msg[i])) | |||||
printf("%c", msg[i]); | |||||
} | |||||
printf("\n"); | |||||
} | |||||
} | |||||
int | |||||
main(int argc, char **argv) | |||||
{ | |||||
int servfd, kq; | |||||
struct sockaddr_in sin; | |||||
int ch, on; | |||||
while ((ch = getopt(argc, argv, "hv")) != -1) { | |||||
switch (ch) { | |||||
case 'v': | |||||
verbose = 1; | |||||
break; | |||||
case 'h': | |||||
default: | |||||
usage(argv[0]); | |||||
break; | |||||
} | |||||
} | |||||
servfd = socket(AF_INET, SOCK_STREAM, 0); | |||||
if (servfd < 0) | |||||
err(EXIT_FAILURE, "socket"); | |||||
memset(&sin, 0, sizeof(sin)); | |||||
sin.sin_family = AF_INET; | |||||
sin.sin_addr.s_addr = INADDR_ANY; | |||||
sin.sin_port = htons(SERVER_PORT); | |||||
on = 1; | |||||
if (setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) | |||||
err(EXIT_FAILURE, "setsockopt"); | |||||
if (bind(servfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) | |||||
err(EXIT_FAILURE, "bind"); | |||||
if (listen(servfd, 5) < 0) | |||||
err(EXIT_FAILURE, "listen"); | |||||
if ((kq = kqueue()) < 0) | |||||
err(EXIT_FAILURE, "kqueue"); | |||||
if (kq_add(kq, servfd) < 0) | |||||
err(EXIT_FAILURE, "kq_add"); | |||||
while (1) { | |||||
struct kevent events[NR_EVENTS]; | |||||
int n, i; | |||||
n = kevent(kq, NULL, 0, events, NR_EVENTS, NULL); | |||||
if (n < 0) | |||||
err(EXIT_FAILURE, "kevent"); | |||||
for (i = 0; i < n; i++) { | |||||
int sd, data; | |||||
sd = events[i].ident; | |||||
data = events[i].data; | |||||
if (sd == servfd) | |||||
accept_new_connection(kq, sd, data); | |||||
else | |||||
receive_data(kq, sd, data); | |||||
} | |||||
} | |||||
return (0); | |||||
} |