Jamoma API  0.6.0.a19
TTNetSocket.cpp
1 /*
2  * Jamoma Network Socket
3  * Copyright © 2010, Tim Place
4  *
5  * License: This code is licensed under the terms of the "New BSD License"
6  * http://creativecommons.org/licenses/BSD/
7  */
8 
9 #include "TTNetSocket.h"
10 #include "TTNetReceive.h"
11 
12 
13 void* TTNetSocketListener(void* anArgument)
14 {
15  TTNetSocketPtr self = (TTNetSocketPtr)anArgument;
16  self->Accept();
17  return NULL;
18 }
19 
20 
21 void* TTNetSocketReceiver(void* anArgument)
22 {
23  TTNetSocketConnectionPtr self = (TTNetSocketConnectionPtr)anArgument;
24  self->Receive();
25  return NULL;
26 }
27 
28 
29 TTNetSocket::TTNetSocket(const TTObjectBasePtr owner, const TTString& address, const TTString& port, const TTSymbol& transport) :
30 mSocketAddressInfo(NULL),
31 mSocketListenerThread(NULL),
32 mOwner(owner)
33 {
34  int err = 0;
35  addrinfo hints;
36  const char* cAddress = NULL;
37 
38  memset(&hints, 0, sizeof(hints));
39  hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
40  if (transport == kTTSym_udp)
41  hints.ai_socktype = SOCK_DGRAM; // UDP datagram sockets
42  else
43  hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
44  if (address.empty())
45  hints.ai_flags = AI_PASSIVE; // fill in my own IP for me
46 
47  // NOTE: we pass NULL as the first parameter to use our own IP address
48  if (!address.empty())
49  cAddress = address.c_str();
50  err = getaddrinfo(cAddress, port.c_str(), &hints, &mSocketAddressInfo);
51  if (err) {
52  TTLogError("TTSocket call to getaddrinfo error: %s \n", gai_strerror(err));
53  return; // if in a constructor, we should really throw and exception...
54  }
55 
56  // getaddrinfo(); //
57  // the above will fill in mSocketAddressInfo with a linked list -- we could use the first one returned, but there may be other addresses in there...
58 
59  // the "res" linked list looking for valid entries instead of just
60  // assuming the first one is good (like many of these examples do.)
61  // See the section on client/server for real examples.]
62 
63  // Hooray -- we now have our address info that we can use to actually open a socket and do something with it
64 
65  mSocketDescriptor = socket(mSocketAddressInfo->ai_family, mSocketAddressInfo->ai_socktype, mSocketAddressInfo->ai_protocol);
66  if (mSocketDescriptor == -1) {
67  TTLogError("TTSocket call to socket() failed! \n");
68  return; // throw and exception here instead?
69  }
70 
71  if (transport == kTTSym_tcp) {
72  int intarg = 1;
73 
74  err = setsockopt(mSocketDescriptor, IPPROTO_TCP, TCP_NODELAY, (char*)&intarg, sizeof(intarg));
75  if (err)
76  TTLogError("TTSocket call to setsockopt() failed! \n");
77  }
78 
79  if (address.empty()) {
80  Bind();
81 
82  // udp is connectionless, and so a call to listen() on a udp socket will fail
83  if (transport == kTTSym_tcp) {
84  err = listen(mSocketDescriptor, kConnectionBacklogSize);
85  if (err) {
86  TTLogError("TTSocket call to listen() failed! \n");
87  return;
88  }
89  mSocketListenerThread = new TTThread(TTNetSocketListener, this);
90  }
91  else {
92  // for udp we skip the listener / accept stuff for connecting and go straight to receiving...
93  TTNetSocketConnectionPtr connection;
94  TTValue v;
95 
96  connection = new TTNetSocketConnection(this); // The connection instance manages its own thread for receiving
97  v[0] = connection;
98  mConnections.append(v); // We need to track connection instances so that we can close them all later
99  }
100 
101 
102  }
103  else
104  Connect();
105 
106 }
107 
108 
109 void TTNetSocket::Accept()
110 {
111  sockaddr_storage client_addr;
112  socklen_t client_addr_size = sizeof(client_addr);
113  int clientSocketDescriptor = 0;
114  TTNetSocketConnectionPtr connection;
115  TTValue v;
116 
117  clientSocketDescriptor = accept(mSocketDescriptor, (sockaddr*)&client_addr, &client_addr_size);
118  if (clientSocketDescriptor == -1) {
119  TTLogError("TTSocket call to accept() failed! \n");
120  goto out;
121  }
122 
123  connection = new TTNetSocketConnection(this); // The connection instance manages its own thread for receiving
124  v[0] = connection;
125  mConnections.append(v); // We need to track connection instances so that we can close them all later
126 
127  // we could send stuff to the client now through clientSocketDescriptor
128  // however, we are simply listening to communication from the client not sending to it
129  // so we don't bother with tracking the clientSocketDescriptor
130 out:
131  TTThread::sleep(100); // TODO: is this interval appropriate?
132 
133 }
134 
135 
136 void TTNetSocketConnection::Receive()
137 {
138  int status;
139  TTValue v, none;
140 
141  while (true) {
142  // TODO: should optimize this by having separate TCP and UDP receive routines
143  if ( ((TTNetReceive*)mSocket->mOwner)->mMode == kTTSym_udp ) {
144  sockaddr remote_addr;
145  socklen_t addr_len;
146 
147  status = recvfrom(mSocket->mSocketDescriptor, mReceiveBuffer, kReceiveBufferSize-1, 0, &remote_addr, &addr_len);
148  }
149  else
150  status = recv(mSocket->mSocketDescriptor, mReceiveBuffer, kReceiveBufferSize, 0);
151 
152  if (status < 0) {
153  // sys_sockerror("tcpreceive_read: recv");
154  // sys_rmpollfn(sockfd);
155  // sys_closesocket(sockfd);
156  // tcpreceive_removeconnection(x, sockfd);
157  // outlet_float(x->x_connectout, --x->x_nconnections);
158  }
159  else if (status == 0) {
160  // post("tcpreceive: EOF on socket %d\n", sockfd);
161  // sys_rmpollfn(sockfd);
162  // sys_closesocket(sockfd);
163  // tcpreceive_removeconnection(x, sockfd);
164  // outlet_float(x->x_connectout, --x->x_nconnections);
165  }
166  else {
167  TTString message(mReceiveBuffer);
168 
169  message.resize(status-1);
170  v[0] = message;
171  mSocket->mOwner->sendMessage(TTSymbol("networkSocketReceive"), v, none);
172  }
173  TTThread::sleep(10); // TODO: is this appropriate?
174  }
175 
176 }
const char * c_str() const
Return a pointer to the internal C-string.
Definition: TTString.h:83
Base class for all first-class Jamoma objects.
Definition: TTObjectBase.h:109
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
void TTFOUNDATION_EXPORT TTLogError(TTImmutableCString message,...)
Platform and host independent method for posting errors.
Definition: TTBase.cpp:572
TTNetReceive is a simple server that echoes network data to a user-provided callback.
Definition: TTNetReceive.h:18
The TTString class is used to represent a string.
Definition: TTString.h:34
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34