Commit dc5496a5 authored by Thorsten Alteholz's avatar Thorsten Alteholz

Imported Upstream version 1.2.0

parents
*~
test/java/EchoClient.class
test/Cperformance/perf
libdontdie.so*
test/Cblackbox/tcp_client
test/Cblackbox/tcp_server
libdontdie-*.tar.xz
language: c
compiler:
- gcc
- clang
before_script:
- echo 'yes' | sudo add-apt-repository ppa:ubuntu-toolchain-r/test
- sudo apt-get update
- sudo apt-get install dh-make
script:
- make all
- make testapps
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2015 Andreas Florath
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.
CC=gcc
CFLAGS+=-fpic -Wall -Wextra -O3 ${CPPFLAGS}
LDFLAGS+=-shared -Wl,-soname,libdontdie.so.0
all: libdontdie.so libdontdie.so.0
testapps: test/Cperformance/perf test/java/EchoClient.class \
test/Cblackbox/tcp_server test/Cblackbox/tcp_client
libdontdie.so.0:
ln -s libdontdie.so libdontdie.so.0
libdontdie.so: src/libdontdie.c
${CC} ${CFLAGS} -o $@ $< ${LDFLAGS} -ldl
test/Cblackbox/tcp_server: test/Cblackbox/tcp_server.c
${CC} ${CFLAGS} -o $@ $<
test/Cblackbox/tcp_client: test/Cblackbox/tcp_client.c
${CC} ${CFLAGS} -o $@ $<
test/Cperformance/perf: test/Cperformance/perf.c
${CC} ${CFLAGS} -o $@ $<
test/java/EchoClient.class: test/java/EchoClient.java
(cd test/java; javac EchoClient.java)
.PHONY: clean
clean:
rm -f libdontdie.so* test/Cperformance/perf test/java/EchoClient.class \
test/Cblackbox/tcp_server test/Cblackbox/tcp_client
# libdontdie
a library that can be pre-loaded and that sets the TCP KEEP-ALIVE flag
whenever `socket(2)' is called
[![Build
Status](https://secure.travis-ci.org/flonatel/libdontdie.png)](http://travis-ci.org/flonatel/pipexec)
## Introduction
This is a complete rewrite of the <a
href="http://libkeepalive.sourceforge.net">libkeepalive</a>.
The main reason for the rewrite was the license of libkeepalive (GPL -
without exceptions) which is always a grey area for a library.
The other reason was, that the libkeepalive does not work with Java -
because of a bug.
## Usage
The library is developed for Linux. The commands given must be
entered in a shell.
```bash
DD_DEBUG=1 DD_TCP_KEEPALIVE_TIME=4 DD_TCP_KEEPALIVE_INTVL=5 \
DD_TCP_KEEPALIVE_PROBES=6 LD_PRELOAD=/usr/lib/libdontdie.so \
java EchoClient 127.0.0.1 22
```
* DD_DEBUG: if set to 1, it prints each call of socket - including the
provided parameters to syslog. Also logged are the decisions if the
keep alive should be set and which parameters are used.
* DD_TCP_KEEPALIVE_TIME, DD_TCP_KEEPALIVE_INTVL,
DD_TCP_KEEPALIVE_PROBES:
These values are related to the appropriate TCP options. There a
<a
href="http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html">good
descriptions around</a>.
* DD_EVAL_ENVIRONMENT_ONCE: if set to 1 or if the environment variable
is not set, all the environment variables are evaluated just once
and not during each socket call.
* DD_USE_KEEPALIVE: if set to 1 or if the environment variable
is not set, tcp keepalive will be switched on. All other values
that are not specified are used from the system.
## Compile and Install
Download the tarball from the release or checkout from git.
Only make and some up to data C compiler is needed.
Run:
```bash
make
```
The library will be created in the current directory. You might want
to copy it into a global folder (like /usr/lib) or copy it locally
to your user or project directory.
```bash
sudo cp libdontdie.so /usr/lib
```
## Example
In the 'test/java' directory there is a simple echo client. Compile:
```bash
javac EchoClient.java
```
This connects to localhost port 22:
```bash
socat echo TCP-LISTEN:7777,fork &
DD_DEBUG=1 DD_TCP_KEEPALIVE_TIME=4 DD_TCP_KEEPALIVE_INTVL=5 DD_TCP_KEEPALIVE_PROBES=6 LD_PRELOAD=/usr/lib/libdontdie.so java EchoClient 127.0.0.1 7777
```
Log output:
```
Mar 4 12:40:20 rs3 java: libdontdie: Initialization
Mar 4 12:40:20 rs3 java: libdontdie: Evaluate environment only once
Mar 4 12:40:20 rs3 java: libdontdie: TCP keepalive is switched on
Mar 4 12:40:20 rs3 java: libdontdie: set TCP_KEEPALIVE_TIME [4]
Mar 4 12:40:20 rs3 java: libdontdie: set TCP_KEEPALIVE_INTVL [5]
Mar 4 12:40:20 rs3 java: libdontdie: set TCP_KEEPALIVE_PROBES [6]
Mar 4 12:40:20 rs3 java: libdontdie: socket() called
Mar 4 12:40:20 rs3 java: libdontdie: domain [AF_INET6]
Mar 4 12:40:20 rs3 java: libdontdie: type [SOCK_STREAM]
Mar 4 12:40:20 rs3 java: libdontdie: type [SOCK_SEQPACKET]
Mar 4 12:40:20 rs3 java: libdontdie: type [SOCK_RAW]
Mar 4 12:40:20 rs3 java: libdontdie: protocol [0]
Mar 4 12:40:20 rs3 java: libdontdie: socket() call returned fd [12]
Mar 4 12:40:20 rs3 java: libdontdie: Parameters check passed
Mar 4 12:40:20 rs3 java: libdontdie: Setting KEEPALIVE for socket
Mar 4 12:40:20 rs3 java: libdontdie: Seting TIME [4]
Mar 4 12:40:20 rs3 java: libdontdie: Seting INTVL [5]
Mar 4 12:40:20 rs3 java: libdontdie: Seting PROBES [6]
Mar 4 12:40:20 rs3 java: libdontdie: Finished; returning to caller [12]
Mar 4 12:40:20 rs3 java: libdontdie: socket() called
Mar 4 12:40:20 rs3 java: libdontdie: domain [AF_INET6]
Mar 4 12:40:20 rs3 java: libdontdie: type [SOCK_STREAM]
Mar 4 12:40:20 rs3 java: libdontdie: type [SOCK_SEQPACKET]
Mar 4 12:40:20 rs3 java: libdontdie: type [SOCK_RAW]
Mar 4 12:40:20 rs3 java: libdontdie: protocol [0]
Mar 4 12:40:20 rs3 java: libdontdie: socket() call returned fd [13]
Mar 4 12:40:20 rs3 java: libdontdie: Parameters check passed
Mar 4 12:40:20 rs3 java: libdontdie: Setting KEEPALIVE for socket
Mar 4 12:40:20 rs3 java: libdontdie: Seting TIME [4]
Mar 4 12:40:20 rs3 java: libdontdie: Seting INTVL [5]
Mar 4 12:40:20 rs3 java: libdontdie: Seting PROBES [6]
Mar 4 12:40:20 rs3 java: libdontdie: Finished; returning to caller [13]
```
TCP Dump output with TCP Keepalives:
```
12:40:20.168088 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [S], seq 2760789742, win 43690, options [mss 65495,sackOK,TS val 1204785362 ecr 0,nop,wscale 7], length 0
12:40:20.168113 IP 127.0.0.1.7777 > 127.0.0.1.40298: Flags [S.], seq 207201762, ack 2760789743, win 43690, options [mss 65495,sackOK,TS val 1204785362 ecr 1204785362,nop,wscale 7], length 0
12:40:20.168135 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204785362 ecr 1204785362], length 0
12:40:24.173260 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204786364 ecr 1204785362], length 0
12:40:24.173348 IP 127.0.0.1.7777 > 127.0.0.1.40298: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204786364 ecr 1204785362], length 0
12:40:29.181281 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204787616 ecr 1204786364], length 0
12:40:34.189248 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204788868 ecr 1204786364], length 0
12:40:39.197282 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204790120 ecr 1204786364], length 0
12:40:44.205280 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204791372 ecr 1204786364], length 0
12:40:49.213271 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204792624 ecr 1204786364], length 0
12:40:54.221254 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [.], ack 1, win 342, options [nop,nop,TS val 1204793876 ecr 1204786364], length 0
12:40:59.229282 IP 127.0.0.1.40298 > 127.0.0.1.7777: Flags [R.], seq 1, ack 1, win 342, options [nop,nop,TS val 1204795128 ecr 1204786364], length 0
```
Shortly after 12:40:24 the firewall was used to drop all packets to port 7777:
```bash
iptables -A INPUT -p tcp --destination-port 7777 -j DROP
```
Every five seconds a keep-alive is sent out and after 6 retries (see
DD_TCP_KEEPALIVE_PROBES=6), the client connection gets into a failed
state:
```
Socket exception java.net.SocketException: Connection timed out
```
## Testing
There are some test programs in the test directory. They can be used
for manual tests.
There are currently no automatic test available. There are two
methods to test this library automatically:
1. Using raw sockets: a big, big overhead
2. Using a firewall / iptables: root permissions needed.
## Performance
Because the most costly operations are only done once - and not during
each socket() call - libdontdie is faster than the libkeepalive.
The next table compares runtimes of libdontdie against libkeepalive
and when none of them are used.
The times are given are the real times as given by the 'time'
command. Percents are computed based on the reference 'raw socket' is
100%. Performance test was done using Debian 8 system, gcc 4.9.2,
Intel i5-3570 CPU, 3.40GHz and 16GByte RAM. 100000000 calls of
'socket()' were executed.
raw socket | libdontdie | libkeepalive
--- | --- | ---
1:51 | 2:31 | 3:16
100% | 136% | 177%
#!/bin/bash
#
# Create release tarball
#
if test $# -ne 1;
then
echo "Usage: create_tar.sh <ReleaseNum>"
exit 1
fi
RELNUM=$1
git tag ${RELNUM}
git archive --format=tar --prefix=libdontdie-${RELNUM}/ ${RELNUM} | xz -9 >libdontdie-${RELNUM}.tar.xz
Colin Hirsch:
help with the documentation
Michael Wins <michael.wins@gmx.de>:
code review of version 1.0.0
Thorsten Alteholz:
patch for makefile: hardening
Thanks to all for your help!
Andreas Florath
.\"
.\" Man page for libdontdie
.\"
.\" For license, see the 'LICENSE' file.
.\"
.TH libdontdie 7 2015-03-07 "TCP keepalive support lib" "TCP keepalive support lib"
.SH NAME
libdontdie \- a library that sets the TCP keep-alive flag when
applications call socket(2)
.SH DESCRIPTION
.B libdontdie
is a shared library that can be injected into any
application with the LD_PRELOAD mechanism. It is completely
transparent to the application and therefore works equally well with
closed-source programs and with languages like Java that do not
natively allow changing the TCP keep-alive parameters.
.SH BACKGROUND
In theory, a TCP connection that is not explicitly closed remains open
forever. In practice, this does not work out when connection-tracking
firewalls along the way drop their state, and all further traffic, for
a connection that was idle for a certain time. The result is that
both the TCP client and server believe the connection to be open but
the firewall inhibits all further communication.
.P
This kind of connection failure will be reported to the participating
applications only after they attempt to send further data and all TCP
timeouts and retransmits expire.
.P
In situation where only one of the communication partners ever sends
anything, e.g. the client waiting for HTTP chunked data, it can be
impossible to distinguish between a connection closed by a firewall
and connected-but-idle.
.P
To handle and/or prevent this kind of situation, TCP has a keep-alive
mechanism. It consists of TCP packets sent periodically over an
otherwise idle connection to "refresh" the associated state along the
whole path. If not acknowledged the same retry-mechanism is used as
for regular data packets, however independent of whether there is any
data to send. Finally all unanswered keep-alive packets leads to a TCP
error that can be detected by the application.
.P
By default the first TCP KEEP ALIVE packet is
send after two hours, which is too long for most scenarios.
.P
The Linux kernel offers the possibility to change the TCP keep-alive
timeout globally for the whole system. However this solution requires
root privileges and does not work when different applications require
different timeouts on the same machine.
.P
In some programming languages an appropriate API call can be used to
enable TCP keep-alive (e.g. setsockopt(2) in C). Other languages like
Java do not support changing these parameters.
.P
.B libdontdie
can be used for applications that are written in languages that do not
support setting the TCP KEEP ALIVE parameters. It can also be used to
enable TCP keep-alive for closed-source or commercial applications.
.B libdontdie
can be used on a case-by-case basis, selectively enabling
TCP keep-alive for some application instances without changing any
source code. All parameters are passed to libdontdie in the form of
environment variables.
.SH TCP KEEP ALIVE PARAMETERS
Three parameters are used to change the TCP keep-alive behaviour. All
parameters are in seconds.
.P
The variable names are taken from the Linux kernel configuration with
a 'DD_' prefix for 'libDontDie'. The corresponding kernel parameters
can be found in '/proc/sys/net/ipv4/'.
.SS DD_TCP_KEEPALIVE_TIME
The time between the last TCP data packet sent and the first TCP KEEP
ALIVE packet / probe.
.SS DD_TCP_KEEPALIVE_INTVL
The interval between two TCP KEEP ALIVE packets / probes.
.SS DD_TCP_KEEPALIVE_PROBES
The number of keep-alive probes sent before the socket enters an error
state.
.SH LIBDONTDIE PARAMETERS
Two additional parameters change the behaviour of libdontdie itself:
.B libdontdie:
.SS DD_DEBUG
If this is set to '1', each 'socket(2)' call will be logged
to syslog - including all parameters and actions performed by
.B libdontdie.
.SS DD_EVAL_ENVIRONMENT_ONCE
If this is set to '1' or not specified at all, all parameters are only
evaluated once during startup. If this parameter is set to '0', each
time a socket call is executed, all parameters are evaluated again.
This makes it possible to change parameters at runtime. When setting
this to '0', there is a bigger per socket call overhead therefore
the performance will decrease.
.P
In languages that do not support setting the TCP KEEP ALIVE
parameters, this is a workaround to enable different setting for
different sockets.
.sp
.RS
.nf
setenv("DD_TCP_KEEPALIVE_TIME", 60);
socket(...);
....
setenv("DD_TCP_KEEPALIVE_TIME", 180);
socket(...);
.SH INSTALLATION LOCATION
Depending on the installation method or the distribution, the
installation directory of
.B libdontdie
might differ.
.P
One way to get the installation directory is using the packet manager
to list all files of the packet (like 'dpkg -L <package_name>').
.P
Typically the library is installed in a directory under /usr/lib,
/usr/lib64, or /usr/lib/<triple>. A typical triple
is 'x86_64-linux-gnu'. Under Debian it is possible to get
the triple with the command
.sp
.RS
.nf
dpkg-architecture -qDEB_HOST_GNU_TYPE
.SH USAGE
All parameters are passed in as environment variables. The
.B libdontdie
itself is preloaded.
.P
The example assumes, that the library is installed
under '/usr/lib/libdontdie.so'. This might be replaced by the real
installation path.
.P
Example: to run the java program EchoClient with special TCP KEEP
ALIVE setting, use:
.sp
.RS
.nf
DD_DEBUG=1 DD_TCP_KEEPALIVE_TIME=4 DD_TCP_KEEPALIVE_INTVL=5 \\
DD_TCP_KEEPALIVE_PROBES=6 LD_PRELOAD=/usr/lib/libdontdie.so \\
java EchoClient 127.0.0.1 22
.SH "SEE ALSO"
.BR socket(2),
.BR setsockopt(2)
.SH HISTORY
The idea was first implemented in libkeepalive by Fabio Busatto.
Because of some limitations regarding functionality and license, it
was completely rewritten, corrected and extended.
.SH AUTHOR
Written by Andreas Florath (andreas@florath.net)
.SH COPYRIGHT
Copyright \(co 2015 by Andreas Florath (andreas@florath.net).
License MIT.
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <dlfcn.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#define LOG(msg...) \
do { \
if (debug > 0) { \
syslog(LOG_USER | LOG_INFO, "libdontdie: " msg); \
} \
} while (0)
int (*socket_call_clib)(int domain, int type, int protocol);
int debug = 0;
int use_keepalive = 1;
int eval_environment_once = 0;
int tcp_keepalive_time = -1;
int tcp_keepalive_intvl = -1;
int tcp_keepalive_probes = -1;
static void eval_environment() {
if (eval_environment_once) {
return;
}
char const *const str_debug = getenv("DD_DEBUG");
if (str_debug != NULL && str_debug[0] == '1') {
debug = 1;
}
LOG("Initialization");
char const *const str_eval_environment_once =
getenv("DD_EVAL_ENVIRONMENT_ONCE");
if (str_eval_environment_once != NULL && *str_eval_environment_once == '0') {
LOG("Evaluate environment for every socket() call");
} else {
eval_environment_once = 1;
LOG("Evaluate environment only once");
}
char const *const str_use_keepalive = getenv("DD_USE_KEEPALIVE");
if (str_use_keepalive != NULL && *str_use_keepalive == '0') {
use_keepalive = 0;
LOG("TCP keepalive is switched off");
} else {
LOG("TCP keepalive is switched on");
}
#define EVAL_ENV(ltype, strtype) \
char const *const str_tcp_keepalive_##ltype = \
getenv("DD_TCP_KEEPALIVE_" strtype); \
if (str_tcp_keepalive_##ltype != NULL) { \
tcp_keepalive_##ltype = atoi(str_tcp_keepalive_##ltype); \
LOG("set TCP_KEEPALIVE_" strtype " [%d]", tcp_keepalive_##ltype); \
}
EVAL_ENV(time, "TIME");
EVAL_ENV(intvl, "INTVL");
EVAL_ENV(probes, "PROBES");
#undef EVAL_ENV
}
__attribute__((constructor)) void libdontdie_init() {
eval_environment();
// Cast it: accoring to the man page this construct must be used:
*(void **)(&socket_call_clib) = dlsym(RTLD_NEXT, "socket");
if (socket_call_clib == NULL) {
LOG("No dynamic symbol with name 'socket' found [%m]");
abort();
}
}
static void log_parameter_domain(int const domain) {
switch (domain) {
case AF_UNIX:
LOG("domain [AF_UNIX]");
break;
case AF_INET:
LOG("domain [AF_INET]");
break;
case AF_INET6:
LOG("domain [AF_INET6]");
break;
case AF_IPX:
LOG("domain [AF_IPX]");
break;
case AF_NETLINK:
LOG("domain [AF_NETLINK]");
break;
case AF_X25:
LOG("domain [AF_X25]");
break;
case AF_AX25:
LOG("domain [AF_AX25]");
break;
case AF_ATMPVC:
LOG("domain [AF_ATMPVC]");
break;
case AF_APPLETALK:
LOG("domain [AF_APPLETALK]");
break;
case AF_PACKET:
LOG("domain [AF_PACKET]");
break;
}
}
static void log_parameter_type(int const type) {
if (type & SOCK_STREAM) {
LOG("type [SOCK_STREAM]");
}
if (type & SOCK_DGRAM) {
LOG("type [SOCK_DGRAM]");
}
if (type & SOCK_SEQPACKET) {
LOG("type [SOCK_SEQPACKET]");
}
if (type & SOCK_RAW) {
LOG("type [SOCK_RAW]");
}
if (type & SOCK_RDM) {
LOG("type [SOCK_RDM]");
}
if (type & SOCK_PACKET) {
LOG("type [SOCK_PACKET]");
}
if (type & SOCK_NONBLOCK) {
LOG("type [NONBLOCK]");
}
if (type & SOCK_CLOEXEC) {
LOG("type [CLOEXEC]");
}
}
static void log_parameters(int domain, int type, int protocol) {
log_parameter_domain(domain);
log_parameter_type(type);
LOG("protocol [%d]", protocol);
}
int socket(int domain, int type, int protocol) {
eval_environment();
if (debug) {
LOG("socket() called");
log_parameters(domain, type, protocol);
}
int const socket_fd = (*socket_call_clib)(domain, type, protocol);
if (socket_fd == -1) {
LOG("Error socket() call [%m]");
return socket_fd;
}
LOG("socket() call returned fd [%d]", socket_fd);
// Check some parameters
if (domain != AF_INET && domain != AF_INET6) {
LOG("Ignoring TCP KEEPALIVE on a non INET4/6 socket");
return socket_fd;
}
if ((type & SOCK_STREAM) == 0) {
LOG("Ignoring TCP KEEPALIVE on a non stream socket");
return socket_fd;
}
LOG("Parameters check passed");
if (use_keepalive == 0) {
LOG("Keepalive switched off by configuration");
return socket_fd;
}
LOG("Setting KEEPALIVE for socket");
int const ssopt_ka = setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE,
&use_keepalive, sizeof(use_keepalive));
if (ssopt_ka == -1) {
LOG("Setting KEEPALIVE returned error [%m]");
return socket_fd;
}
#define SET_SOCK_OPT(ltype, strtype, tcp_type) \
if (tcp_keepalive_##ltype >= 0) { \
LOG("Seting " strtype " [%d]", tcp_keepalive_##ltype); \
int const ssopt = \
setsockopt(socket_fd, SOL_TCP, tcp_type, &tcp_keepalive_##ltype, \
sizeof(tcp_keepalive_##ltype)); \
if (ssopt == -1) { \
LOG("Setting " strtype " returned error [%m]"); \
} \
}
SET_SOCK_OPT(time, "TIME", TCP_KEEPIDLE);
SET_SOCK_OPT(intvl, "INTVL", TCP_KEEPINTVL);
SET_SOCK_OPT(probes, "PROBES", TCP_KEEPCNT);
#undef SET_SOCK_OPT
LOG("Finished; returning to caller [%d]", socket_fd);
return socket_fd;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
int client_port = 0;
int client_socket = 0;
while (!feof(stdin)) {
int const c = getchar();
if (c == EOF) {
break;
}
switch (c) {
case 'c': {
char str_port[256];
char const *fs = fgets(str_port, 256, stdin);
if (fs == 0) {
perror("fgets port number");
exit(1);
}
client_port = atoi(str_port);
// printf("%d\n", client_port);
client_socket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = htons(client_port);
connect(client_socket, (struct sockaddr *)&servaddr, sizeof(servaddr));
printf("1\n");
} break;
case 'w': {
char text[256];
char const *fs = fgets(text, 256, stdin);
if (fs == 0) {
perror("text");
exit(1);
}
size_t const cnt = strlen(text) - 1;
ssize_t const w = write(client_socket, text, cnt);
if (w == -1) {
perror("write");
exit(1);
}
} break;
case 'r': {
char buffer[256];
bzero(buffer, 256);
int const n = read(client_socket, buffer, 255);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("%s\n", buffer);
} break;
case 'l': {
close(client_socket);
} break;
case 'p':
printf("pong\n");
break;
case '\n':
break;
default:
printf("Unknown command [%c]\n", c);
abort();
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
static int open_socket() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
/* Initialize socket structure */
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(0);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
return sockfd;
}
static int listen_and_accept(int sockfd) {