Address Structure
The Berkeley socket interface provides a set of defined C structures that helps you handle different endpoint addresses and other socket programming duties.The most important structure you
will work with is the sockaddr_in structure. Its purpose is to provide a standard way of handling endpoint addresses for network communications. The sockaddr_in structure contains both an IP address and a protocol port number. The structure looks like this:
struct sockaddr_in {
short sin_family; /* type of address */
u_short sin_port; /* protocol port number */
/* network byte ordered */
struct in_addr sin_addr; /* net address for the remote host */
/* network byte ordered */
char sin_zero[8]; /* unused, set to zero */
};
struct in_addr {
uint32_t s_addr; /* Internet address */
};
Using Socket Functions
Core Functions
You can’t have a client without having a server first.
socket()
socket() is a critical function. Without it, your application cannot communicate with a network. socket() is used to create a network endpoint, and it returns a socket descriptor that is used later by other functions.
int socket(int domain, int type, int protocol);
socket() returns a –1 if there is an error. Otherwise, the value returned is the descriptor to the socket.
bind()
Now that we’ve created our socket, we need to bind it to an address. Simply creating a socket doesn’t do anything—you have to give it a place to go, much like creating a file doesn’t do much unless you save it to disk somewhere. The bind() function is used to bind the socket to a given address once the socket is created.
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
The first value bind() needs is our socket descriptor. The second parameter is a pointer to the sockaddr structure we discussed earlier, while the third parameter is the length of the sockaddr structure. In this example, we’ll use the constant INADDR_ANY to signal that we want to bind to all of our local host’s addresses.
listen()
The next function we need is listen(). This function tells our socket that we’re ready to accept connections, and it also specifies a maximum number of connections that can be queued before connections are refused.
int listen(int s, int backlog);
backlog is the value that determines the connection queue. A typical value for backlog is 5, though it can be larger as needed. Some systems, however, such as BSD-based systems, limit the value at 5, so if you’re going to port your application to other environments besides Linux, don’t rely on backlog being greater than 5. Note that with Linux versions after 2.2, only completely established sockets are counted in the queue, instead of incomplete connection requests.
The call to listen() is successful, a value of 0 is returned:
accept()
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
accept() takes our socket descriptor, a pointer to the address structure, and
the length of the address structure as its parameters. The key thing to remember
with this function is that it will typically run in an endless loop. The only time
you want a network server to stop listening is when you shut it down manually.
Otherwise, your application should be listening and accepting connections con-
stantly.
accept() is a blocking function. That means your application will wait at the accept() function call until a connection request is received from a client. This behavior is configurable, but the default blocking behavior is typically the desired behavior.
The structures passed to accept() are client related, not server related. As you can see in the preceding example, the second and third parameters passed to accept() are locations for storing information about the client, not about the server. When started, the value for the client name is set to 0, as is the length of the name. When a call from accept() returns, both of the structures should be populated with correct information. On success, accept() returns a new socket descriptor for the new connection.
write()
ssize_t write(int fd, const void *buf, size_t count);
write() writes up to count bytes of the contents of buf to the designated descriptor, in this case our child socket.
close()
Lastly, we need to do some cleanup. Use the close() function to close your sockets:
int close(int fd)
The first thing to do is close our child socket as soon as the write to the client is done.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.