summaryrefslogtreecommitdiff
path: root/comm/Datagram.cc
blob: 66c40dfc0594e2876151285ccba35f917b721397 (plain)
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
160
161
162
163
164
/*
 *	The Unix Channel
 *
 *	by Michel Beaudouin-Lafon
 *
 *	Copyright 1990-1997
 *	Laboratoire de Recherche en Informatique (LRI)
 *
 *	Datagrams
 *
 *	$Id$
 *	$CurLog$
 */

#include "Datagram.h"
#include "MsgBuffer.h"
#include <stdlib.h>
#include <sys/socket.h>

/*?class IvlDatagram
A datagram socket can send to and receive from any other datagram
socket, unless it is connected.  Thus, establishing a datagram
connection is simple.

Datagram sockets are not reliable: messages can be lost, duplicated,
or be received in a different order.  They keep the message
boundaries: when \var{n} bytes are written, you will read at most
\var{n} bytes; but if you ask to read less than \var{n} bytes, then
the end of the message will be lost.

When a datagram socket is not connected, you must provide an address
when you send a message; when a message is read, the address of the
sender can be retrieved (with function \fun{From}).  When a datagram
socket is connected, messages can only be sent to and read from the
connected address. The \fun{Read} and \fun{Write} calls can be used in
this case.

Before any data can be sent or received, the socket must be set up with \fun{Setup},
or \fun{Open} followed by \fun{Bind} and \fun{Connect} if necessary. 
?*/

/*?
These constructors are similar to those of the class \typ{IvlSocket}.
?*/
IvlDatagram :: IvlDatagram (IvlAddress* bound, IvlAddress* connected)
: IvlSocket (bound, connected),
  FAddr (0)
{
}

/*?nodoc?*/
IvlDatagram :: IvlDatagram (const IvlDatagram& d)
: IvlSocket (d),
  FAddr (d.FAddr)
{
}

/*?nodoc?*/
IvlDatagram :: ~IvlDatagram ()
{
}

#ifdef DOC
/*?
Return the address of the sender of the last received message.
?*/
IvlAddress*
IvlDatagram :: From ()
{ }

#endif /* DOC */

/*?nodoc?*/
IvlChannel*
IvlDatagram :: Copy () const
{
	return new IvlDatagram (*this);
}

/*?nodoc?*/
int
IvlDatagram :: SockType ()
{
	return SOCK_DGRAM;
}

/*?
Send \var{len} bytes of \var{buf} on the datagram, to a destination address \var{to}.
Return the number of bytes actually transferred, or -1 if a system error occurred.
If the datagram is connected, you must use \fun{Write} instead.
?*/
int
IvlDatagram :: Send (byte* buf, int len, IvlAddress& to)
{
	return sendto (FilDes (), (char*) buf, len, 0, to.GetSockAddr (), to.Length ());
}

/*?
Receive at most \var{len} bytes into \var{buf}.
Return the number of bytes actually transferred, or -1 if a system error occurred.
The address of the sender can be retrieved with \fun{From}.
If the socket is connected, you must use \fun{Read} instead.
?*/
int
IvlDatagram :: Receive (byte* buf, int len)
{
	GEN_ADDR addr;
	socklen_t alen = sizeof (addr);
	int ret;
	
	ret = recvfrom (Fd, (char*) buf, len, 0, &addr.sa, &alen);
	if (ret < 0)
		return ret;
	
	FAddr = IvlAddress::Decode (&addr, alen);
	return ret;
}

/*?
This is equivalent to \fun{Send(buf, len, From ())}:
it sends \fun{len} bytes to the sender of the last received message.
If there is no such sender, it returns -1.
?*/
int
IvlDatagram :: Reply (byte* buf, int len)
{
	if (! FAddr)
		return -1;
	return sendto (FilDes (), (char*) buf, len, 0, FAddr->GetSockAddr (), FAddr->Length ());
}

/*?nextdoc?*/
int
IvlDatagram :: Send (IvlMsgBuffer& buf, IvlAddress& to, bool peek)
{
	int n = Send (buf.Buffer (), buf.BufLength (), to);
	if (! peek)
		buf.Flush (n);
	return n;
}

/*?nextdoc?*/
int
IvlDatagram :: Receive (IvlMsgBuffer& buf)
{
	int n = Receive (buf.Free (), buf.FreeLength ());
	if (n > 0)
		buf.More (n);
	return n;
}

/*?
The same functions but with a \typ{IvlMsgBuffer} argument instead of a byte pointer and size.
As usual, if \var{peek} is true the buffer is not flushed.
?*/
int
IvlDatagram :: Reply (IvlMsgBuffer& buf, bool peek)
{
	int n = Reply (buf.Buffer (), buf.BufLength ());
	if (! peek)
		buf.Flush (n);
	return n;
}