Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cppdap
Project
Overview
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
cppdap
Commits
13c9e7d4
Commit
13c9e7d4
authored
Jun 05, 2020
by
Ben Clayton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement timeouts for dap::Socket::connect
Fixes: #24
parent
261d62d9
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
159 additions
and
32 deletions
+159
-32
CMakeLists.txt
CMakeLists.txt
+1
-0
network.h
include/dap/network.h
+8
-4
network.cpp
src/network.cpp
+4
-2
socket.cpp
src/socket.cpp
+90
-25
socket.h
src/socket.h
+5
-1
socket_test.cpp
src/socket_test.cpp
+51
-0
No files found.
CMakeLists.txt
View file @
13c9e7d4
...
@@ -195,6 +195,7 @@ if(CPPDAP_BUILD_TESTS)
...
@@ -195,6 +195,7 @@ if(CPPDAP_BUILD_TESTS)
${
CPPDAP_SRC_DIR
}
/network_test.cpp
${
CPPDAP_SRC_DIR
}
/network_test.cpp
${
CPPDAP_SRC_DIR
}
/optional_test.cpp
${
CPPDAP_SRC_DIR
}
/optional_test.cpp
${
CPPDAP_SRC_DIR
}
/session_test.cpp
${
CPPDAP_SRC_DIR
}
/session_test.cpp
${
CPPDAP_SRC_DIR
}
/socket_test.cpp
${
CPPDAP_SRC_DIR
}
/typeinfo_test.cpp
${
CPPDAP_SRC_DIR
}
/typeinfo_test.cpp
${
CPPDAP_SRC_DIR
}
/variant_test.cpp
${
CPPDAP_SRC_DIR
}
/variant_test.cpp
${
CPPDAP_GOOGLETEST_DIR
}
/googletest/src/gtest-all.cc
${
CPPDAP_GOOGLETEST_DIR
}
/googletest/src/gtest-all.cc
...
...
include/dap/network.h
View file @
13c9e7d4
...
@@ -24,12 +24,16 @@ class ReaderWriter;
...
@@ -24,12 +24,16 @@ class ReaderWriter;
namespace
net
{
namespace
net
{
// connect() connects to the given TCP address and port.
// connect() connects to the given TCP address and port.
std
::
shared_ptr
<
ReaderWriter
>
connect
(
const
char
*
addr
,
int
port
);
// If timeoutMillis is non-zero and no connection was made before timeoutMillis
// milliseconds, then nullptr is returned.
std
::
shared_ptr
<
ReaderWriter
>
connect
(
const
char
*
addr
,
int
port
,
uint32_t
timeoutMillis
=
0
);
// Server implements a basic TCP server.
// Server implements a basic TCP server.
class
Server
{
class
Server
{
//
IgnoreErrors
matches the OnError signature, and does nothing.
//
ignoreErrors()
matches the OnError signature, and does nothing.
static
inline
void
I
gnoreErrors
(
const
char
*
)
{}
static
inline
void
i
gnoreErrors
(
const
char
*
)
{}
public
:
public
:
using
OnError
=
std
::
function
<
void
(
const
char
*
)
>
;
using
OnError
=
std
::
function
<
void
(
const
char
*
)
>
;
...
@@ -45,7 +49,7 @@ class Server {
...
@@ -45,7 +49,7 @@ class Server {
// onError will be called for any connection errors.
// onError will be called for any connection errors.
virtual
bool
start
(
int
port
,
virtual
bool
start
(
int
port
,
const
OnConnect
&
callback
,
const
OnConnect
&
callback
,
const
OnError
&
onError
=
I
gnoreErrors
)
=
0
;
const
OnError
&
onError
=
i
gnoreErrors
)
=
0
;
// stop() stops listening for connections.
// stop() stops listening for connections.
// stop() is implicitly called on destruction.
// stop() is implicitly called on destruction.
...
...
src/network.cpp
View file @
13c9e7d4
...
@@ -92,8 +92,10 @@ std::unique_ptr<Server> Server::create() {
...
@@ -92,8 +92,10 @@ std::unique_ptr<Server> Server::create() {
return
std
::
unique_ptr
<
Server
>
(
new
Impl
());
return
std
::
unique_ptr
<
Server
>
(
new
Impl
());
}
}
std
::
shared_ptr
<
ReaderWriter
>
connect
(
const
char
*
addr
,
int
port
)
{
std
::
shared_ptr
<
ReaderWriter
>
connect
(
const
char
*
addr
,
return
Socket
::
connect
(
addr
,
std
::
to_string
(
port
).
c_str
());
int
port
,
uint32_t
timeoutMillis
)
{
return
Socket
::
connect
(
addr
,
std
::
to_string
(
port
).
c_str
(),
timeoutMillis
);
}
}
}
// namespace net
}
// namespace net
...
...
src/socket.cpp
View file @
13c9e7d4
...
@@ -32,6 +32,8 @@ namespace {
...
@@ -32,6 +32,8 @@ namespace {
std
::
atomic
<
int
>
wsaInitCount
=
{
0
};
std
::
atomic
<
int
>
wsaInitCount
=
{
0
};
}
// anonymous namespace
}
// anonymous namespace
#else
#else
#include <fcntl.h>
#include <unistd.h>
namespace
{
namespace
{
using
SOCKET
=
int
;
using
SOCKET
=
int
;
}
// anonymous namespace
}
// anonymous namespace
...
@@ -39,27 +41,27 @@ using SOCKET = int;
...
@@ -39,27 +41,27 @@ using SOCKET = int;
namespace
{
namespace
{
constexpr
SOCKET
InvalidSocket
=
static_cast
<
SOCKET
>
(
-
1
);
constexpr
SOCKET
InvalidSocket
=
static_cast
<
SOCKET
>
(
-
1
);
}
// anonymous namespace
static
void
init
()
{
class
dap
::
Socket
::
Shared
:
public
dap
::
ReaderWriter
{
public
:
static
void
init
()
{
#if defined(_WIN32)
#if defined(_WIN32)
if
(
wsaInitCount
++
==
0
)
{
if
(
wsaInitCount
++
==
0
)
{
WSADATA
winsockData
;
WSADATA
winsockData
;
(
void
)
WSAStartup
(
MAKEWORD
(
2
,
2
),
&
winsockData
);
(
void
)
WSAStartup
(
MAKEWORD
(
2
,
2
),
&
winsockData
);
}
#endif
}
}
#endif
}
static
void
term
()
{
static
void
term
()
{
#if defined(_WIN32)
#if defined(_WIN32)
if
(
--
wsaInitCount
==
0
)
{
if
(
--
wsaInitCount
==
0
)
{
WSACleanup
();
WSACleanup
();
}
#endif
}
}
#endif
}
}
// anonymous namespace
class
dap
::
Socket
::
Shared
:
public
dap
::
ReaderWriter
{
public
:
static
std
::
shared_ptr
<
Shared
>
create
(
const
char
*
address
,
const
char
*
port
)
{
static
std
::
shared_ptr
<
Shared
>
create
(
const
char
*
address
,
const
char
*
port
)
{
init
();
init
();
...
@@ -123,24 +125,45 @@ class dap::Socket::Shared : public dap::ReaderWriter {
...
@@ -123,24 +125,45 @@ class dap::Socket::Shared : public dap::ReaderWriter {
setsockopt
(
s
,
IPPROTO_TCP
,
TCP_NODELAY
,
(
char
*
)
&
enable
,
sizeof
(
enable
));
setsockopt
(
s
,
IPPROTO_TCP
,
TCP_NODELAY
,
(
char
*
)
&
enable
,
sizeof
(
enable
));
}
}
// dap::ReaderWriter compliance
bool
setBlocking
(
bool
blocking
)
{
bool
isOpen
()
{
SOCKET
s
=
socket
();
SOCKET
s
=
socket
();
if
(
s
==
InvalidSocket
)
{
if
(
s
==
InvalidSocket
)
{
return
false
;
return
false
;
}
}
#if defined(_WIN32)
u_long
mode
=
blocking
?
0
:
1
;
return
ioctlsocket
(
s
,
FIONBIO
,
&
mode
)
==
NO_ERROR
;
#else
auto
arg
=
fcntl
(
s
,
F_GETFL
,
nullptr
);
if
(
arg
<
0
)
{
return
false
;
}
arg
=
blocking
?
(
arg
&
~
O_NONBLOCK
)
:
(
arg
|
O_NONBLOCK
);
return
fcntl
(
s
,
F_SETFL
,
arg
)
>=
0
;
#endif
}
bool
errored
()
{
SOCKET
s
=
socket
();
if
(
s
==
InvalidSocket
)
{
return
true
;
}
char
error
=
0
;
char
error
=
0
;
socklen_t
len
=
sizeof
(
error
);
socklen_t
len
=
sizeof
(
error
);
getsockopt
(
s
,
SOL_SOCKET
,
SO_ERROR
,
&
error
,
&
len
);
getsockopt
(
s
,
SOL_SOCKET
,
SO_ERROR
,
&
error
,
&
len
);
if
(
error
!=
0
)
{
if
(
error
!=
0
)
{
sock
.
compare_exchange_weak
(
s
,
InvalidSocket
);
sock
.
compare_exchange_weak
(
s
,
InvalidSocket
);
return
fals
e
;
return
tru
e
;
}
}
return
tru
e
;
return
fals
e
;
}
}
// dap::ReaderWriter compliance
bool
isOpen
()
{
return
!
errored
();
}
void
close
()
{
void
close
()
{
SOCKET
s
=
sock
.
exchange
(
InvalidSocket
);
SOCKET
s
=
sock
.
exchange
(
InvalidSocket
);
if
(
s
!=
InvalidSocket
)
{
if
(
s
!=
InvalidSocket
)
{
...
@@ -195,7 +218,7 @@ Socket::Socket(const char* address, const char* port)
...
@@ -195,7 +218,7 @@ Socket::Socket(const char* address, const char* port)
return
;
return
;
}
}
if
(
listen
(
socket
,
1
)
!=
0
)
{
if
(
listen
(
socket
,
0
)
!=
0
)
{
shared
.
reset
();
shared
.
reset
();
return
;
return
;
}
}
...
@@ -205,6 +228,7 @@ std::shared_ptr<ReaderWriter> Socket::accept() const {
...
@@ -205,6 +228,7 @@ std::shared_ptr<ReaderWriter> Socket::accept() const {
if
(
shared
)
{
if
(
shared
)
{
SOCKET
socket
=
shared
->
socket
();
SOCKET
socket
=
shared
->
socket
();
if
(
socket
!=
InvalidSocket
)
{
if
(
socket
!=
InvalidSocket
)
{
init
();
auto
out
=
std
::
make_shared
<
Shared
>
(
::
accept
(
socket
,
0
,
0
));
auto
out
=
std
::
make_shared
<
Shared
>
(
::
accept
(
socket
,
0
,
0
));
out
->
setOptions
();
out
->
setOptions
();
return
out
;
return
out
;
...
@@ -228,13 +252,54 @@ void Socket::close() const {
...
@@ -228,13 +252,54 @@ void Socket::close() const {
}
}
std
::
shared_ptr
<
ReaderWriter
>
Socket
::
connect
(
const
char
*
address
,
std
::
shared_ptr
<
ReaderWriter
>
Socket
::
connect
(
const
char
*
address
,
const
char
*
port
)
{
const
char
*
port
,
uint32_t
timeoutMillis
)
{
auto
shared
=
Shared
::
create
(
address
,
port
);
auto
shared
=
Shared
::
create
(
address
,
port
);
if
(
::
connect
(
shared
->
socket
(),
shared
->
info
->
ai_addr
,
if
(
!
shared
)
{
(
int
)
shared
->
info
->
ai_addrlen
)
==
0
)
{
return
nullptr
;
return
shared
;
}
}
return
{};
if
(
timeoutMillis
==
0
)
{
if
(
::
connect
(
shared
->
socket
(),
shared
->
info
->
ai_addr
,
(
int
)
shared
->
info
->
ai_addrlen
)
==
0
)
{
return
shared
;
}
return
nullptr
;
}
auto
s
=
shared
->
socket
();
if
(
s
==
InvalidSocket
)
{
return
nullptr
;
}
if
(
!
shared
->
setBlocking
(
false
))
{
return
nullptr
;
}
auto
res
=
::
connect
(
s
,
shared
->
info
->
ai_addr
,
(
int
)
shared
->
info
->
ai_addrlen
);
if
(
res
==
0
)
{
return
shared
->
setBlocking
(
true
)
?
shared
:
nullptr
;
}
const
auto
microseconds
=
timeoutMillis
*
1000
;
fd_set
fdset
;
FD_ZERO
(
&
fdset
);
FD_SET
(
s
,
&
fdset
);
timeval
tv
;
tv
.
tv_sec
=
microseconds
/
1000000
;
tv
.
tv_usec
=
microseconds
-
(
tv
.
tv_sec
*
1000000
);
res
=
select
(
static_cast
<
int
>
(
s
+
1
),
nullptr
,
&
fdset
,
nullptr
,
&
tv
);
if
(
res
<=
0
)
{
return
nullptr
;
}
if
(
shared
->
errored
())
{
return
nullptr
;
}
return
shared
->
setBlocking
(
true
)
?
shared
:
nullptr
;
}
}
}
// namespace dap
}
// namespace dap
src/socket.h
View file @
13c9e7d4
...
@@ -26,8 +26,12 @@ class Socket {
...
@@ -26,8 +26,12 @@ class Socket {
public
:
public
:
class
Shared
;
class
Shared
;
// connect() connects to the given TCP address and port.
// If timeoutMillis is non-zero and no connection was made before
// timeoutMillis milliseconds, then nullptr is returned.
static
std
::
shared_ptr
<
ReaderWriter
>
connect
(
const
char
*
address
,
static
std
::
shared_ptr
<
ReaderWriter
>
connect
(
const
char
*
address
,
const
char
*
port
);
const
char
*
port
,
uint32_t
timeoutMillis
);
Socket
(
const
char
*
address
,
const
char
*
port
);
Socket
(
const
char
*
address
,
const
char
*
port
);
bool
isOpen
()
const
;
bool
isOpen
()
const
;
...
...
src/socket_test.cpp
0 → 100644
View file @
13c9e7d4
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "socket.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <chrono>
#include <vector>
TEST
(
Socket
,
ConnectTimeout
)
{
const
char
*
port
=
"19021"
;
const
int
timeoutMillis
=
200
;
const
int
maxAttempts
=
1024
;
using
namespace
std
::
chrono
;
auto
server
=
dap
::
Socket
(
"localhost"
,
port
);
std
::
vector
<
std
::
shared_ptr
<
dap
::
ReaderWriter
>>
connections
;
for
(
int
i
=
0
;
i
<
maxAttempts
;
i
++
)
{
auto
start
=
system_clock
::
now
();
auto
connection
=
dap
::
Socket
::
connect
(
"localhost"
,
port
,
timeoutMillis
);
auto
end
=
system_clock
::
now
();
if
(
!
connection
)
{
auto
timeTakenMillis
=
duration_cast
<
milliseconds
>
(
end
-
start
).
count
();
ASSERT_GE
(
timeTakenMillis
+
20
,
// +20ms for a bit of timing wiggle room
timeoutMillis
);
return
;
}
// Keep hold of the connections to saturate any incoming socket buffers.
connections
.
emplace_back
(
std
::
move
(
connection
));
}
FAIL
()
<<
"Failed to test timeout after "
<<
maxAttempts
<<
" attempts"
;
}
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