Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
533a6b02
Commit
533a6b02
authored
Aug 28, 2011
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tcp_connect: generic library for establishing TCP connections
parent
0c0400b6
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
524 additions
and
0 deletions
+524
-0
Makefile.am
Makefile.am
+11
-0
tcp_connect.c
src/tcp_connect.c
+252
-0
tcp_connect.h
src/tcp_connect.h
+96
-0
run_tcp_connect.c
test/run_tcp_connect.c
+165
-0
No files found.
Makefile.am
View file @
533a6b02
...
...
@@ -299,6 +299,7 @@ src_mpd_SOURCES = \
src/client_message.c
\
src/client_subscribe.h
\
src/client_subscribe.c
\
src/tcp_connect.c src/tcp_connect.h
\
src/tcp_socket.c src/tcp_socket.h
\
src/udp_server.c src/udp_server.h
\
src/server_socket.c
\
...
...
@@ -904,6 +905,7 @@ noinst_PROGRAMS = \
$(C_TESTS)
\
test
/read_conf
\
test
/run_resolver
\
test
/run_tcp_connect
\
test
/run_input
\
test
/dump_playlist
\
test
/run_decoder
\
...
...
@@ -932,6 +934,15 @@ test_run_resolver_LDADD = $(MPD_LIBS) \
test_run_resolver_SOURCES
=
test
/run_resolver.c
\
src/resolver.c
test_run_tcp_connect_CPPFLAGS
=
$(AM_CPPFLAGS)
test_run_tcp_connect_LDADD
=
$(MPD_LIBS)
\
$(GLIB_LIBS)
test_run_tcp_connect_SOURCES
=
test
/run_tcp_connect.c
\
src/io_thread.c src/io_thread.h
\
src/fd_util.c
\
src/resolver.c
\
src/tcp_connect.c
test_run_input_CPPFLAGS
=
$(AM_CPPFLAGS)
\
$(ARCHIVE_CFLAGS)
\
$(INPUT_CFLAGS)
...
...
src/tcp_connect.c
0 → 100644
View file @
533a6b02
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "tcp_connect.h"
#include "fd_util.h"
#include "io_thread.h"
#include "glib_compat.h"
#include "glib_socket.h"
#include <assert.h>
#include <errno.h>
#ifdef WIN32
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#else
#include <sys/socket.h>
#include <unistd.h>
#endif
struct
tcp_connect
{
const
struct
tcp_connect_handler
*
handler
;
void
*
handler_ctx
;
int
fd
;
GSource
*
source
;
unsigned
timeout_ms
;
GSource
*
timeout_source
;
};
static
bool
is_in_progress_errno
(
int
e
)
{
#ifdef WIN32
return
e
==
WSAEINPROGRESS
||
e
==
WSAEWOULDBLOCK
;
#else
return
e
==
EINPROGRESS
;
#endif
}
static
gboolean
tcp_connect_event
(
G_GNUC_UNUSED
GIOChannel
*
source
,
G_GNUC_UNUSED
GIOCondition
condition
,
gpointer
data
)
{
struct
tcp_connect
*
c
=
data
;
assert
(
c
->
source
!=
NULL
);
assert
(
c
->
timeout_source
!=
NULL
);
/* clear the socket source */
g_source_unref
(
c
->
source
);
c
->
source
=
NULL
;
/* delete the timeout source */
g_source_destroy
(
c
->
timeout_source
);
g_source_unref
(
c
->
timeout_source
);
c
->
timeout_source
=
NULL
;
/* obtain the connect result */
int
s_err
=
0
;
socklen_t
s_err_size
=
sizeof
(
s_err
);
if
(
getsockopt
(
c
->
fd
,
SOL_SOCKET
,
SO_ERROR
,
(
char
*
)
&
s_err
,
&
s_err_size
)
<
0
)
s_err
=
errno
;
if
(
s_err
==
0
)
{
/* connection established successfully */
c
->
handler
->
success
(
c
->
fd
,
c
->
handler_ctx
);
}
else
{
/* there was an I/O error; close the socket and pass
the error to the handler */
close_socket
(
c
->
fd
);
GError
*
error
=
g_error_new_literal
(
g_file_error_quark
(),
s_err
,
g_strerror
(
s_err
));
c
->
handler
->
error
(
error
,
c
->
handler_ctx
);
}
return
false
;
}
static
gboolean
tcp_connect_timeout
(
gpointer
data
)
{
struct
tcp_connect
*
c
=
data
;
assert
(
c
->
source
!=
NULL
);
assert
(
c
->
timeout_source
!=
NULL
);
/* clear the timeout source */
g_source_unref
(
c
->
timeout_source
);
c
->
timeout_source
=
NULL
;
/* delete the socket source */
g_source_destroy
(
c
->
source
);
g_source_unref
(
c
->
source
);
c
->
source
=
NULL
;
/* report timeout to handler */
c
->
handler
->
timeout
(
c
->
handler_ctx
);
return
false
;
}
static
gpointer
tcp_connect_init
(
gpointer
data
)
{
struct
tcp_connect
*
c
=
data
;
/* create a connect source */
GIOChannel
*
channel
=
g_io_channel_new_socket
(
c
->
fd
);
c
->
source
=
g_io_create_watch
(
channel
,
G_IO_OUT
);
g_io_channel_unref
(
channel
);
g_source_set_callback
(
c
->
source
,
(
GSourceFunc
)
tcp_connect_event
,
c
,
NULL
);
g_source_attach
(
c
->
source
,
io_thread_context
());
/* create a timeout source */
if
(
c
->
timeout_ms
>
0
)
c
->
timeout_source
=
io_thread_timeout_add
(
c
->
timeout_ms
,
tcp_connect_timeout
,
c
);
return
NULL
;
}
void
tcp_connect_address
(
const
struct
sockaddr
*
address
,
size_t
address_length
,
unsigned
timeout_ms
,
const
struct
tcp_connect_handler
*
handler
,
void
*
ctx
,
struct
tcp_connect
**
handle_r
)
{
assert
(
address
!=
NULL
);
assert
(
address_length
>
0
);
assert
(
handler
!=
NULL
);
assert
(
handler
->
success
!=
NULL
);
assert
(
handler
->
error
!=
NULL
);
assert
(
handler
->
canceled
!=
NULL
);
assert
(
handler
->
timeout
!=
NULL
||
timeout_ms
==
0
);
assert
(
handle_r
!=
NULL
);
assert
(
*
handle_r
==
NULL
);
int
fd
=
socket_cloexec_nonblock
(
address
->
sa_family
,
SOCK_STREAM
,
0
);
if
(
fd
<
0
)
{
GError
*
error
=
g_error_new_literal
(
g_file_error_quark
(),
errno
,
g_strerror
(
errno
));
handler
->
error
(
error
,
ctx
);
return
;
}
int
ret
=
connect
(
fd
,
address
,
address_length
);
if
(
ret
>=
0
)
{
/* quick connect, no I/O thread */
handler
->
success
(
fd
,
ctx
);
return
;
}
if
(
!
is_in_progress_errno
(
errno
))
{
GError
*
error
=
g_error_new_literal
(
g_file_error_quark
(),
errno
,
g_strerror
(
errno
));
close_socket
(
fd
);
handler
->
error
(
error
,
ctx
);
return
;
}
/* got EINPROGRESS, use the I/O thread to wait for the
operation to finish */
struct
tcp_connect
*
c
=
g_new
(
struct
tcp_connect
,
1
);
c
->
handler
=
handler
;
c
->
handler_ctx
=
ctx
;
c
->
fd
=
fd
;
c
->
source
=
NULL
;
c
->
timeout_ms
=
timeout_ms
;
c
->
timeout_source
=
NULL
;
*
handle_r
=
c
;
io_thread_call
(
tcp_connect_init
,
c
);
}
static
gpointer
tcp_connect_cancel_callback
(
gpointer
data
)
{
struct
tcp_connect
*
c
=
data
;
assert
((
c
->
source
==
NULL
)
==
(
c
->
timeout_source
==
NULL
));
if
(
c
->
source
==
NULL
)
return
NULL
;
/* delete the socket source */
g_source_destroy
(
c
->
source
);
g_source_unref
(
c
->
source
);
c
->
source
=
NULL
;
/* delete the timeout source */
g_source_destroy
(
c
->
timeout_source
);
g_source_unref
(
c
->
timeout_source
);
c
->
timeout_source
=
NULL
;
/* close the socket */
close_socket
(
c
->
fd
);
/* notify the handler */
c
->
handler
->
canceled
(
c
->
handler_ctx
);
return
NULL
;
}
void
tcp_connect_cancel
(
struct
tcp_connect
*
c
)
{
if
(
c
->
source
==
NULL
)
return
;
io_thread_call
(
tcp_connect_cancel_callback
,
c
);
}
void
tcp_connect_free
(
struct
tcp_connect
*
c
)
{
assert
(
c
->
source
==
NULL
);
g_free
(
c
);
}
src/tcp_connect.h
0 → 100644
View file @
533a6b02
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_TCP_CONNECT_H
#define MPD_TCP_CONNECT_H
#include <glib.h>
struct
sockaddr
;
struct
tcp_connect_handler
{
/**
* The connection was established successfully.
*
* @param fd a file descriptor that must be closed with
* close_socket() when finished
*/
void
(
*
success
)(
int
fd
,
void
*
ctx
);
/**
* An error has occurred. The method is responsible for
* freeing the GError.
*/
void
(
*
error
)(
GError
*
error
,
void
*
ctx
);
/**
* The connection could not be established in the specified
* time span.
*/
void
(
*
timeout
)(
void
*
ctx
);
/**
* The operation was canceled before a result was available.
*/
void
(
*
canceled
)(
void
*
ctx
);
};
struct
tcp_connect
;
/**
* Establish a TCP connection to the specified address.
*
* Note that the result may be available before this function returns.
*
* The caller must free this object with tcp_connect_free().
*
* @param timeout_ms time out after this number of milliseconds; 0
* means no timeout
* @param handle_r a handle that can be used to cancel the operation;
* the caller must initialize it to NULL
*/
void
tcp_connect_address
(
const
struct
sockaddr
*
address
,
size_t
address_length
,
unsigned
timeout_ms
,
const
struct
tcp_connect_handler
*
handler
,
void
*
ctx
,
struct
tcp_connect
**
handle_r
);
/**
* Cancel the operation. It is possible that the result is delivered
* before the operation has been canceled; in that case, the
* canceled() handler method will not be invoked.
*
* Even after calling this function, tcp_connect_free() must still be
* called to free memory.
*/
void
tcp_connect_cancel
(
struct
tcp_connect
*
handle
);
/**
* Free memory used by this object.
*
* This function is not thread safe. It must not be called while
* other threads are still working with it. If no callback has been
* invoked so far, then you must call tcp_connect_cancel() to release
* I/O thread resources, before calling this function.
*/
void
tcp_connect_free
(
struct
tcp_connect
*
handle
);
#endif
test/run_tcp_connect.c
0 → 100644
View file @
533a6b02
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "resolver.h"
#include "io_thread.h"
#include "tcp_connect.h"
#include "fd_util.h"
#include <assert.h>
#include <stdlib.h>
#ifdef WIN32
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif
static
struct
tcp_connect
*
handle
;
static
GMutex
*
mutex
;
static
GCond
*
cond
;
static
bool
done
,
success
;
static
void
my_tcp_connect_success
(
int
fd
,
G_GNUC_UNUSED
void
*
ctx
)
{
assert
(
!
done
);
assert
(
!
success
);
close_socket
(
fd
);
g_print
(
"success
\n
"
);
g_mutex_lock
(
mutex
);
done
=
success
=
true
;
g_cond_signal
(
cond
);
g_mutex_unlock
(
mutex
);
}
static
void
my_tcp_connect_error
(
GError
*
error
,
G_GNUC_UNUSED
void
*
ctx
)
{
assert
(
!
done
);
assert
(
!
success
);
g_printerr
(
"error: %s
\n
"
,
error
->
message
);
g_error_free
(
error
);
g_mutex_lock
(
mutex
);
done
=
true
;
g_cond_signal
(
cond
);
g_mutex_unlock
(
mutex
);
}
static
void
my_tcp_connect_timeout
(
G_GNUC_UNUSED
void
*
ctx
)
{
assert
(
!
done
);
assert
(
!
success
);
g_printerr
(
"timeout
\n
"
);
g_mutex_lock
(
mutex
);
done
=
true
;
g_cond_signal
(
cond
);
g_mutex_unlock
(
mutex
);
}
static
void
my_tcp_connect_canceled
(
G_GNUC_UNUSED
void
*
ctx
)
{
assert
(
!
done
);
assert
(
!
success
);
g_printerr
(
"canceled
\n
"
);
g_mutex_lock
(
mutex
);
done
=
true
;
g_cond_signal
(
cond
);
g_mutex_unlock
(
mutex
);
}
static
const
struct
tcp_connect_handler
my_tcp_connect_handler
=
{
.
success
=
my_tcp_connect_success
,
.
error
=
my_tcp_connect_error
,
.
timeout
=
my_tcp_connect_timeout
,
.
canceled
=
my_tcp_connect_canceled
,
};
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
!=
2
)
{
g_printerr
(
"Usage: run_tcp_connect IP:PORT
\n
"
);
return
1
;
}
GError
*
error
=
NULL
;
struct
addrinfo
*
ai
=
resolve_host_port
(
argv
[
1
],
80
,
0
,
SOCK_STREAM
,
&
error
);
if
(
ai
==
NULL
)
{
g_printerr
(
"%s
\n
"
,
error
->
message
);
g_error_free
(
error
);
return
EXIT_FAILURE
;
}
/* initialize GLib */
g_thread_init
(
NULL
);
/* initialize MPD */
io_thread_init
();
if
(
!
io_thread_start
(
&
error
))
{
freeaddrinfo
(
ai
);
g_printerr
(
"%s"
,
error
->
message
);
g_error_free
(
error
);
return
EXIT_FAILURE
;
}
/* open the connection */
mutex
=
g_mutex_new
();
cond
=
g_cond_new
();
tcp_connect_address
(
ai
->
ai_addr
,
ai
->
ai_addrlen
,
5000
,
&
my_tcp_connect_handler
,
NULL
,
&
handle
);
freeaddrinfo
(
ai
);
if
(
handle
!=
NULL
)
{
g_mutex_lock
(
mutex
);
while
(
!
done
)
g_cond_wait
(
cond
,
mutex
);
g_mutex_unlock
(
mutex
);
tcp_connect_free
(
handle
);
}
g_cond_free
(
cond
);
g_mutex_free
(
mutex
);
/* deinitialize everything */
io_thread_deinit
();
return
EXIT_SUCCESS
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment