1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

introduce keepalive option and make common socket options function

Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
Andy Green 2013-02-09 12:25:31 +08:00
parent da1fb0b89f
commit a690cd066e
8 changed files with 124 additions and 28 deletions

View file

@ -162,3 +162,25 @@ copy interesting headers by handling LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION
callback, for clients there's a new callback just for this purpose
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH.
TCP Keepalive
-------------
It is possible for a connection which is not being used to send to die
silently somewhere between the peer and the side not sending. In this case
by default TCP will just not report anything and you will never get any more
incoming data or sign the link is dead until you try to send.
To deal with getting a notification of that situation, you can choose to
enable TCP keepalives on all libwebsockets sockets, when you create the
context.
To enable keepalive, set the ka_time member of the context creation parameter
struct to a nonzero value (in seconds) at context creation time. You should
also fill ka_probes and ka_interval in that case.
With keepalive enabled, the TCP layer will send control packets that should
stimulate a response from the peer without affecting link traffic. If the
response is not coming, the socket will announce an error at poll() forcing
a close.

View file

@ -10,6 +10,14 @@ User api additions
"1.1 9e7f737", representing the library version from configure.ac
and the git HEAD hash the library was built from
- TCP Keepalive can now optionally be applied to all lws sockets, with
controllable timeout, number of probes and probe interval. This
enables detection of idle connections which are logically okay, but
are in fact dead, due to network connectivity issues at the server,
client, or any intermediary. By default it's not enabled, but you
can enable it by setting a non-zero timeout (in seconds) at the new
ka_time member at context creation time.
User api changes
----------------

View file

@ -10,10 +10,6 @@ struct libwebsocket *__libwebsocket_client_connect_2(
int n;
int plen = 0;
char pkt[512];
int opt = 1;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
struct protoent *tcp_proto;
#endif
lwsl_client("__libwebsocket_client_connect_2\n");
#ifndef LWS_NO_EXTENSIONS
@ -41,7 +37,8 @@ struct libwebsocket *__libwebsocket_client_connect_2(
* prepare the actual connection (to the proxy, if any)
*/
lwsl_client("__libwebsocket_client_connect_2: address %s", wsi->c_address);
lwsl_client("__libwebsocket_client_connect_2: address %s\n",
wsi->c_address);
server_hostent = gethostbyname(wsi->c_address);
if (server_hostent == NULL) {
@ -61,16 +58,6 @@ struct libwebsocket *__libwebsocket_client_connect_2(
server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
bzero(&server_addr.sin_zero, 8);
/* Disable Nagle */
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
setsockopt(wsi->sock, SOL_TCP, TCP_NODELAY,
(const void *)&opt, sizeof(opt));
#else
tcp_proto = getprotobyname("TCP");
setsockopt(wsi->sock, tcp_proto->p_proto, TCP_NODELAY,
&opt, sizeof(opt));
#endif
if (connect(wsi->sock, (struct sockaddr *)&server_addr,
sizeof(struct sockaddr)) == -1) {
lwsl_debug("Connect failed\n");
@ -80,6 +67,12 @@ struct libwebsocket *__libwebsocket_client_connect_2(
lwsl_client("connected\n");
if (lws_set_socket_options(context, wsi->sock)) {
lwsl_err("Failed to set wsi socket options\n");
close(wsi->sock);
goto oom4;
}
insert_wsi_socket_into_fds(context, wsi);
/* we are connected to server, or proxy */

View file

@ -519,6 +519,60 @@ int libwebsockets_get_random(struct libwebsocket_context *context,
return n;
}
int lws_set_socket_options(struct libwebsocket_context *context, int fd)
{
int optval = 1;
socklen_t optlen = sizeof(optval);
#ifdef WIN32
unsigned long optl = 0;
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
struct protoent *tcp_proto;
#endif
if (context->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const void *)&optval, optlen) < 0)
return 1;
/* set the keepalive conditions we want on it too */
optval = context->ka_time;
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
return 1;
optval = context->ka_probes;
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
(const void *)&optval, optlen) < 0)
return 1;
optval = context->ka_interval;
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
(const void *)&optval, optlen) < 0)
return 1;
}
/* Disable Nagle */
optval = 1;
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
#else
tcp_proto = getprotobyname("TCP");
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
#endif
/* We are nonblocking... */
#ifdef WIN32
ioctlsocket(fd, FIONBIO, &optl);
#else
fcntl(fd, F_SETFL, O_NONBLOCK);
#endif
return 0;
}
int lws_send_pipe_choked(struct libwebsocket *wsi)
{
struct pollfd fds;

View file

@ -742,6 +742,13 @@ struct libwebsocket_extension {
* @options: 0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK
* @user: optional user pointer that can be recovered via the context
* pointer using libwebsocket_context_user
* @ka_time: 0 for no keepalive, otherwise apply this keepalive timeout to
* all libwebsocket sockets, client or server
* @ka_probes: if ka_time was nonzero, after the timeout expires how many
* times to try to get a response from the peer before giving up
* and killing the connection
* @ka_interval: if ka_time was nonzero, how long to wait before each ka_probes
* attempt
*/
struct lws_context_creation_info {
@ -756,6 +763,10 @@ struct lws_context_creation_info {
int uid;
unsigned int options;
void *user;
int ka_time;
int ka_probes;
int ka_interval;
};
LWS_EXTERN

View file

@ -265,6 +265,10 @@ struct libwebsocket_context {
int listen_service_fd;
int listen_service_extraseen;
int ka_time;
int ka_probes;
int ka_interval;
#ifdef LWS_LATENCY
unsigned long worst_latency;
char worst_latency_info[256];
@ -489,6 +493,9 @@ user_callback_handle_rxflow(callback_function, struct libwebsocket_context * con
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len);
extern int
lws_set_socket_options(struct libwebsocket_context *context, int fd);
#ifndef LWS_OPENSSL_SUPPORT
unsigned char *

View file

@ -133,7 +133,6 @@ int lws_server_socket_service(struct libwebsocket_context *context,
unsigned int clilen;
struct sockaddr_in cli_addr;
int n;
int opt = 1;
ssize_t len;
#ifdef LWS_OPENSSL_SUPPORT
int m;
@ -217,18 +216,7 @@ int lws_server_socket_service(struct libwebsocket_context *context,
break;
}
/* Disable Nagle */
opt = 1;
setsockopt(accept_fd, IPPROTO_TCP, TCP_NODELAY,
(const void *)&opt, sizeof(opt));
/* We are nonblocking... */
#ifdef WIN32
opt = 0;
ioctlsocket(accept_fd, FIONBIO, (unsigned long *)&opt );
#else
fcntl(accept_fd, F_SETFL, O_NONBLOCK);
#endif
lws_set_socket_options(context, accept_fd);
/*
* look at who we connected to and give user code a chance

View file

@ -968,6 +968,9 @@ all sessions, etc, if it wants
&nbsp; &nbsp; <i>int</i> <b>uid</b>;<br>
&nbsp; &nbsp; <i>unsigned int</i> <b>options</b>;<br>
&nbsp; &nbsp; <i>void *</i> <b>user</b>;<br>
&nbsp; &nbsp; <i>int</i> <b>ka_time</b>;<br>
&nbsp; &nbsp; <i>int</i> <b>ka_probes</b>;<br>
&nbsp; &nbsp; <i>int</i> <b>ka_interval</b>;<br>
};<br>
<h3>Members</h3>
<dl>
@ -1005,5 +1008,15 @@ else ignored
<dt><b>user</b>
<dd>optional user pointer that can be recovered via the context
pointer using libwebsocket_context_user
<dt><b>ka_time</b>
<dd>0 for no keepalive, otherwise apply this keepalive timeout to
all libwebsocket sockets, client or server
<dt><b>ka_probes</b>
<dd>if ka_time was nonzero, after the timeout expires how many
times to try to get a response from the peer before giving up
and killing the connection
<dt><b>ka_interval</b>
<dd>if ka_time was nonzero, how long to wait before each ka_probes
attempt
</dl>
<hr>