VxWorks select() Guide: Build a Multi-Client TCP Server
🚀 Introduction #
In the previous tutorial, you built a basic TCP client/server. But real systems rarely talk to just one client.
So how do you handle multiple simultaneous connections without spawning a task per client?
The answer is select().
This guide shows how to build a multi-client TCP server in VxWorks using a single task, keeping resource usage low while maintaining responsiveness.
🧩 What is select()?
#
select() is a system call that allows you to monitor multiple sockets at once and determine which ones are ready for I/O.
Instead of blocking on a single socket, your task can:
- Wait for new connections
- Receive data from multiple clients
- React only when something actually happens
Function Prototype #
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
Key Idea #
You give select() a set of file descriptors, and it tells you:
- Which sockets are ready to read
- Which are ready to write
- Which have errors
This is the foundation of event-driven networking in embedded systems.
⚙️ Why Use select() in VxWorks? #
In resource-constrained environments, this matters a lot.
Without select()
#
- One task per client
- High memory usage
- Context-switch overhead
With select()
#
- Single task handles all clients
- Lower memory footprint
- Deterministic behavior
This makes it ideal for RTOS-based systems like VxWorks.
💻 Example: Multi-Client TCP Echo Server #
This example demonstrates a server that:
- Listens on port 6000
- Accepts multiple clients
- Echoes received data back to each client
Code Example #
#include <vxWorks.h>
#include <taskLib.h>
#include <sockLib.h>
#include <inetLib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SERVER_PORT 6000
#define MAX_CLIENTS 5
void multiClientServerTask()
{
int serverSock, clientSock, maxFd, activity, i;
int clientSockets[MAX_CLIENTS];
struct sockaddr_in serverAddr, clientAddr;
fd_set readFds;
char buffer[256];
int addrLen = sizeof(clientAddr);
// Initialize client sockets
for (i = 0; i < MAX_CLIENTS; i++)
clientSockets[i] = 0;
// Create server socket
serverSock = socket(AF_INET, SOCK_STREAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(SERVER_PORT);
bind(serverSock, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
listen(serverSock, 3);
printf("Server listening on port %d\n", SERVER_PORT);
while (1)
{
FD_ZERO(&readFds);
FD_SET(serverSock, &readFds);
maxFd = serverSock;
// Add client sockets
for (i = 0; i < MAX_CLIENTS; i++)
{
if (clientSockets[i] > 0)
FD_SET(clientSockets[i], &readFds);
if (clientSockets[i] > maxFd)
maxFd = clientSockets[i];
}
// Wait for activity
activity = select(maxFd + 1, &readFds, NULL, NULL, NULL);
// New connection
if (FD_ISSET(serverSock, &readFds))
{
clientSock = accept(serverSock,
(struct sockaddr*)&clientAddr,
&addrLen);
printf("New client connected (fd=%d)\n", clientSock);
for (i = 0; i < MAX_CLIENTS; i++)
{
if (clientSockets[i] == 0)
{
clientSockets[i] = clientSock;
break;
}
}
}
// Handle client data
for (i = 0; i < MAX_CLIENTS; i++)
{
int sd = clientSockets[i];
if (FD_ISSET(sd, &readFds))
{
int bytes = recv(sd, buffer, sizeof(buffer) - 1, 0);
if (bytes <= 0)
{
printf("Client disconnected (fd=%d)\n", sd);
close(sd);
clientSockets[i] = 0;
}
else
{
buffer[bytes] = '\0';
printf("Client %d: %s\n", sd, buffer);
send(sd, buffer, bytes, 0);
}
}
}
}
}
void usrAppInit(void)
{
taskSpawn("tMultiServer", 100, 0, 8000,
(FUNCPTR)multiClientServerTask,
0,0,0,0,0,0,0,0,0,0);
}
📝 Code Walkthrough #
1. Server Setup #
- Create socket using
socket() - Bind to port 6000
- Start listening with
listen()
2. File Descriptor Management #
Each loop iteration:
-
Clear the set with
FD_ZERO -
Add:
- Server socket → for new connections
- Client sockets → for incoming data
3. Waiting for Events #
select(maxFd + 1, &readFds, NULL, NULL, NULL);
This blocks until:
- A new client connects
- A client sends data
4. Accepting Clients #
If the server socket is ready:
clientSock = accept(...)
- Add the new socket to the
clientSockets[]array - Track it for future reads
5. Handling Client Data #
For each client:
- If data is received → process it
- If connection closed → clean up
⚡ Example Output #
Server listening on port 6000
New client connected (fd=4)
Client 4: Hello
New client connected (fd=5)
Client 5: Hi Server
Client 4: How are you?
🔍 Key Takeaways #
select()enables event-driven networking in VxWorks- One task can efficiently handle multiple clients
- Ideal for embedded systems with limited resources
- Avoids overhead of thread-per-connection models
⚠️ Practical Tips #
- Always track maxFd correctly
- Use timeouts if you need periodic processing
- Avoid large
MAX_CLIENTSunless necessary - For high scalability, consider
poll()or advanced mechanisms (if available)
✅ Wrap-Up #
You’ve now learned how to:
- Use
select()in VxWorks - Build a multi-client TCP server
- Efficiently manage multiple sockets in a single task
This pattern is widely used in embedded networking and forms the basis of more advanced designs.