forked from keithw/datagrump
-
Notifications
You must be signed in to change notification settings - Fork 0
/
socket.cc
159 lines (134 loc) · 3.77 KB
/
socket.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <cassert>
#include "socket.hh"
using namespace std;
using namespace Network;
/* Default constructor */
Socket::Socket()
: sock_(-1),
timestamps_enabled( true ) {}
/* Construct socket of specific type */
Socket::Socket( int sock_type )
: sock_( socket( AF_INET, sock_type, 0 ) ),
timestamps_enabled( true )
{
if ( sock_ < 0 ) {
perror( "socket" );
exit( 1 );
}
/* Ask for timestamps */
int ts_opt = 1;
if ( setsockopt( sock_, SOL_SOCKET, SO_TIMESTAMPNS, &ts_opt,
sizeof( ts_opt ) ) < 0 ) {
perror( "setsockopt" );
exit( 1 );
}
}
/* Accept connection request */
Socket Socket::accept( void ) const
{
int64_t acc = ::accept( sock_, nullptr, nullptr);
if ( acc < 0 ) {
perror( "accept" );
exit( 1 );
}
Socket tmp_socket;
tmp_socket.set_fd( acc );
return tmp_socket;
}
/* listen for requests */
void Socket::listen(int BACKLOG ) const
{
if ( ::listen( sock_, BACKLOG) == -1) {
perror( "listen" );
exit( 1 );
}
}
/* Connect to ip/port (typically used by client) */
Socket Socket::connect( const Address & addr ) const
{
int64_t connect_fd = ::connect( sock_, (sockaddr *)&addr.sockaddr(),
sizeof( addr.sockaddr() ) );
if (connect_fd < 0) {
fprintf( stderr, "Error connecting to %s\n", addr.str().c_str() );
perror( "connect" );
exit( 1 );
}
Socket tmp_socket;
tmp_socket.set_fd( connect_fd );
return tmp_socket;
}
/* Bind to port (typically used by server) */
void Socket::bind( const Address & addr ) const
{
if ( ::bind( sock_, (sockaddr *)&addr.sockaddr(),
sizeof( addr.sockaddr() ) ) < 0 ) {
fprintf( stderr, "Error binding to %s\n", addr.str().c_str() );
perror( "bind" );
exit( 1 );
}
}
/* Send packet */
void Socket::send( Packet & packet ) const
{
packet.set_send_timestamp();
string payload( packet.str() );
ssize_t bytes_sent = sendto( sock_, payload.data(),
payload.size(), 0,
(sockaddr *)&packet.addr().sockaddr(),
sizeof( packet.addr().sockaddr() ) );
if ( bytes_sent != static_cast<ssize_t>( payload.size() ) ) {
perror( "sendto" );
throw string( "sendto error" );
}
}
/* Receive a packet and associated timestamp */
Packet Socket::recv( void ) const
{
static const int RECEIVE_MTU = 2048;
/* receive source address, timestamp, and payload in msghdr structure */
struct sockaddr_in packet_remote_addr;
struct msghdr header;
struct iovec msg_iovec;
char msg_payload[ RECEIVE_MTU ];
char msg_control[ RECEIVE_MTU ];
/* receive source address */
header.msg_name = &packet_remote_addr;
header.msg_namelen = sizeof( packet_remote_addr );
/* receive payload */
msg_iovec.iov_base = msg_payload;
msg_iovec.iov_len = RECEIVE_MTU;
header.msg_iov = &msg_iovec;
header.msg_iovlen = 1;
/* receive timestamp */
header.msg_control = msg_control;
header.msg_controllen = RECEIVE_MTU;
/* receive flags */
header.msg_flags = 0;
ssize_t received_len = recvmsg( sock_, &header, 0 );
if ( received_len < 0 ) {
perror( "recvmsg" );
throw string( "recvmsg" );
}
if ( header.msg_flags & MSG_TRUNC ) {
perror( "recvmsg" );
throw string( "Received oversize datagram" );
}
/* verify presence of timestamp */
if (timestamps_enabled) {
struct cmsghdr *ts_hdr = CMSG_FIRSTHDR( &header );
assert( ts_hdr );
assert( ts_hdr->cmsg_level == SOL_SOCKET );
assert( ts_hdr->cmsg_type == SO_TIMESTAMPNS );
return Packet( Address( packet_remote_addr ),
string( msg_payload, received_len ),
*(struct timespec *)CMSG_DATA( ts_hdr ) );
} else {
struct timespec ts;
return Packet( Address( packet_remote_addr ),
string( msg_payload, received_len ),
ts);
}
}