Skip to content

Commit 3894ddc

Browse files
authored
Refactor Drv::Ip::UdpSocket to clean up state members and remove dependency on dynamic memory (#3728)
* Refactor UdpSocket to remove SocketState and the use of dynamic memory allocation. * Replacing some ASSERTs with proper SocketIpStatus. Adding some input argument validation. Adding some better logging. * Refactoring UdpSocket to remove redundant m_recv_port and m_recv_hostname members * Fixing some documentation and renaming a variable to be more consistent * Adding ADDRSTRLEN to spelling expect * Incorporating PR fixes and adding a new UT for zero length datagram * Replacing a strcpy with a Fw::StringUtils::string_copy * Removing incorrect UDP statuses in empty datagram test * Fixing bug in implementation of UdpSocket by overriding IpSocket implementation of send and recv to properly handle 0-length datagrams * Incorporating PR fixes. Adding function argument checks for send/recv. * Fixing build errors * Incorporating PR comments * Forgot to add change for IpSocket * Fixing asserts and removing virtual from recv that we shouldn't have to override. * Spelling
1 parent 0a9c057 commit 3894ddc

File tree

6 files changed

+236
-94
lines changed

6 files changed

+236
-94
lines changed

.github/actions/spelling/expect.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ACTIVERATEGROUPCFG
99
ACTIVERATEGROUPTESTER
1010
ACTIVERATEGROUPIMPLTESTER
1111
ACTIVETEXTLOGGERIMPL
12+
ADDRSTRLEN
1213
adminlist
1314
aleem
1415
ALLEXTERNALS

Drv/Ip/IpSocket.cpp

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ SocketIpStatus IpSocket::open(SocketDescriptor& socketDescriptor) {
129129
}
130130

131131
SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
132+
FW_ASSERT(socketDescriptor.fd != -1, static_cast<FwAssertArgType>(socketDescriptor.fd));
133+
FW_ASSERT(data != nullptr);
134+
FW_ASSERT(size > 0);
135+
132136
U32 total = 0;
133137
I32 sent = 0;
134138
// Attempt to send out data and retry as necessary
@@ -161,40 +165,53 @@ SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8
161165
}
162166

163167
SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data, U32& req_read) {
164-
I32 size = 0;
165-
// Try to read until we fail to receive data
166-
for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (size <= 0); i++) {
167-
errno = 0;
168-
// Attempt to recv out data
169-
size = this->recvProtocol(socketDescriptor, data, req_read);
168+
//TODO: Uncomment FW_ASSERT for socketDescriptor.fd once we fix TcpClientTester to not pass in uninitialized socketDescriptor
169+
// FW_ASSERT(socketDescriptor.fd != -1, static_cast<FwAssertArgType>(socketDescriptor.fd));
170+
FW_ASSERT(data != nullptr);
170171

171-
// Nothing to be received
172-
if ((size == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
173-
req_read = 0;
174-
return SOCK_NO_DATA_AVAILABLE;
175-
}
172+
173+
I32 bytes_received_or_status; // Stores the return value from recvProtocol
176174

177-
// Error is EINTR, just try again
178-
if ((size == -1) && (errno == EINTR)) {
179-
continue;
180-
}
181-
// Zero bytes read reset or bad ef means we've disconnected
182-
else if (size == 0 || ((size == -1) && ((errno == ECONNRESET) || (errno == EBADF)))) {
183-
req_read = static_cast<U32>(size);
184-
return SOCK_DISCONNECTED;
185-
}
186-
// Error returned, and it wasn't an interrupt, nor a disconnect
187-
else if (size == -1) {
188-
req_read = static_cast<U32>(size);
189-
return SOCK_READ_ERROR; // Stop recv task on error
175+
// Loop primarily for EINTR. Other conditions should lead to an earlier exit.
176+
for (U32 i = 0; i < SOCKET_MAX_ITERATIONS; i++) {
177+
errno = 0;
178+
// Pass the current value of req_read (max buffer size) to recvProtocol.
179+
// recvProtocol returns bytes read or -1 on error.
180+
bytes_received_or_status = this->recvProtocol(socketDescriptor, data, req_read);
181+
182+
if (bytes_received_or_status > 0) {
183+
// Successfully read data
184+
req_read = static_cast<U32>(bytes_received_or_status);
185+
return SOCK_SUCCESS;
186+
} else if (bytes_received_or_status == 0) {
187+
// Handle zero return based on protocol-specific behavior
188+
req_read = 0;
189+
return this->handleZeroReturn();
190+
} else { // bytes_received_or_status == -1, an error occurred
191+
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
192+
// Non-blocking socket would block, or SO_RCVTIMEO timeout occurred.
193+
req_read = 0;
194+
return SOCK_NO_DATA_AVAILABLE;
195+
} else if ((errno == ECONNRESET) || (errno == EBADF)) {
196+
// Connection reset or bad file descriptor.
197+
req_read = 0;
198+
return SOCK_DISCONNECTED; // Or a more specific error like SOCK_READ_ERROR
199+
} else {
200+
// Other socket read error.
201+
req_read = 0;
202+
return SOCK_READ_ERROR;
203+
}
190204
}
191205
}
192-
req_read = static_cast<U32>(size);
193-
// Prevent interrupted socket being viewed as success
194-
if (size == -1) {
195-
return SOCK_INTERRUPTED_TRY_AGAIN;
196-
}
197-
return SOCK_SUCCESS;
206+
// If the loop completes, it means SOCKET_MAX_ITERATIONS of EINTR occurred.
207+
req_read = 0;
208+
return SOCK_INTERRUPTED_TRY_AGAIN;
209+
}
210+
211+
SocketIpStatus IpSocket::handleZeroReturn() {
212+
// For TCP (which IpSocket primarily serves as a base for, or when not overridden),
213+
// a return of 0 from ::recv means the peer has performed an orderly shutdown.
214+
return SOCK_DISCONNECTED;
198215
}
199216

200217
} // namespace Drv

Drv/Ip/IpSocket.hpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class IpSocket {
114114
* \param size: size of data to send
115115
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
116116
*/
117-
SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size);
117+
virtual SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size);
118118
/**
119119
* \brief receive data from the IP socket from the given buffer
120120
*
@@ -200,13 +200,24 @@ class IpSocket {
200200

201201
/**
202202
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
203-
* \param socket: socket descriptor to recv from
203+
* \param socketDescriptor: socket descriptor to recv from
204204
* \param data: data pointer to fill
205205
* \param size: size of data buffer
206206
* \return: size of data received, or -1 on error.
207207
*/
208208
virtual I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) = 0;
209209

210+
/**
211+
* \brief Handle zero return from recvProtocol
212+
*
213+
* This method is called when recvProtocol returns 0. The default implementation
214+
* treats this as a disconnection (appropriate for TCP). Subclasses can override
215+
* this to provide different behavior.
216+
*
217+
* @return SocketIpStatus Status to return from recv
218+
*/
219+
virtual SocketIpStatus handleZeroReturn();
220+
210221
U32 m_timeoutSeconds;
211222
U32 m_timeoutMicroseconds;
212223
U16 m_port; //!< IP address port used

0 commit comments

Comments
 (0)