Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: July 6, 2024
The accept() function in the socket API is a component of server-side socket programming code.
In this tutorial, we’ll explore socket API and learn how the accept() function works in socket programming.
The Socket API is a component of socket programming that encapsulates many low-level operations:
Referring to the diagram above, socket API consists of three components: socket creation, socket connection, and socket communication.
Specifically, the accept() function is a component of socket listening, which, in turn, is part of socket creation.
Let’s explore the socket creation component.
In a server application, socket creation can be separated into two parts: socket binding and socket listening.
Socket binding involves creating a server socket using the socket() function and then using the bind() function to bind (or associate) the socket with a specific port. Additionally, if the server has multiple network interfaces (therefore multiple IP addresses), there’s an option to bind the socket to a specific network interface or IP address:
algorithm SocketCreation(port, ip):
// INPUT
// port = port number to listen
// ip = server IP address (optional, can be 0 or 0.0.0.0)
// OUTPUT
// client socket file descriptor
server_fd <- create socket file descriptor using socket()
bind the socket with server port and ip using bind()
...
At this point, we’ve assigned the socket with the server’s port (and IP address).
Afterward, we call the listen() function to start listening for incoming connections:
algorithm SocketCreation(port, ip):
...
listen for incoming connections using listen()
while true:
new_socket <- accept incoming client request using accept()
return new_socket
end
After a successful listen() call, the socket then enters a “listening” state. Additionally, some high-level languages, like Java, implicitly invoke the bind() and listen() APIs in the background to simplify the API calls.
Finally, we call the accept() function to handle incoming client requests. The accept() function is a blocking function by default, meaning it’ll wait and only return until there’s an incoming client request.
Subsequently, the accept() function creates a new socket, or to be precise, a socket file descriptor. The socket file descriptor is an integer value and unique per client connection. Most importantly, it’s associated with various values in the background by the OS:
By embedding the client and server profile information into each socket file descriptor, the server app – or in this case, the OS – knows where to send the response.
In the previous section, we learned that the accept() function call is blocking by default. This is also called a synchronous operation. This mode could be useful for some use cases, such as for:
However, we can configure the accept() function to be non-blocking or asynchronous too. This method is particularly useful for various use cases, for example:
Some high-level languages, like Java, have their own libraries for asynchronous calls.
In this tutorial, we explored the socket programming components, specifically the socket listening component, which we call the accept() function.
The accept() function handles the incoming requests by creating unique socket file descriptors for each client connection. Subsequently, the operating system associates these socket file descriptors with various values of the client and server information in the background. As a result, the application server – or more precisely, the OS – knows where to send the response.
Finally, we learned that we can configure the accept() in either blocking (synchronous) or non-blocking (asynchronous) mode and discussed the real-life use cases for each mode.