I just got asked this question and couldn't answer it, I looked at how I've been coding it and was really confused.
This is how I have been programming the accept() call in a server:
struct sockaddr_in client;
size=sizeof(client);
if(( nds=accept(ds,(struct sockaddr*)&client,&size)) <0)
{
perror("accept");
close(ds);
exit(-1);
}
Where ds is socket descriptor
I know the second parameter of accept is a pointer to the struct but don't know why it should be empty.
Weel, it is an output parameter, you can send it or not, but when you send it, it will be filled with the the connecting socket.
Take a look here http://pubs.opengroup.org/onlinepubs/009695399/functions/accept.html
If address is not a null pointer, the address of the peer for the
accepted connection shall be stored in the sockaddr structure pointed
to by address, and the length of this address shall be stored in the
object pointed to by address_len.
Related
So, I'm creating a server in C which uses UDP, and I want to listen for incoming packets from many sources. Therefore, when I call ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict), the 5th parameter, that which contains the sender's information, may vary.
Is there a way to receive the packets without knowing each individual client's address information? And, is this possible with C's library?
Here's my code:
int file_descriptor;
char data[1024];
int bytes_recved;
sockaddr_in iDontKnow;
socklen_t addr_len = sizeof(iDontKnow);
if ((bytes_recved = recvfrom(file_descriptor, data, strlen(data), 0, (struct sockaddr*)&iDontKnow, &addr_len)) < 0) {
perror("Failed to receive data");
}
I noticed that when receiving data with Java's DatagramSocket and DatagramPacket classes, the DatagramSocket's receive function took in a parameter of type DatagramPacket. This DatagramPacket, however, only held the object in which to place the data. So, why does C's implementation of UDP receiving require that you know the sender's information?
Is there a way to receive the packets without knowing each individual client's address information?
Well, you don't need to know the sender information beforehand, anyway. Once a packet is received, the sender information (if available) will be stored into address.
From the man page,
ssize_t recvfrom(int socket, void *restrict buffer, size_t length,
int flags, struct sockaddr *restrict address,
socklen_t *restrict address_len);
[...] If the address argument is not a null pointer and the protocol provides the source address of messages, the source address of the received message shall be stored in the sockaddr structure pointed to by the address argument, and the length of this address shall be stored in the object pointed to by the address_len argument.
Regarding the why part, in case of connectionless sockets, unless you know of the sender address for a packet in a communication, you cannot reply or respond to the sender. So, it is required to know the sender info specifically in connectionless mode and there comes recvfrom() which, along with the received data, gives us the info about the sender, also.
EDIT:
In your code
recvfrom(file_descriptor, data, strlen(data), 0, (struct sockaddr*)&iDontKnow, &addr_len)
is wrong, as strlen(data) is UB, as you're trying to count the length of an uninitialized char array, which is not qualified to be a string. It invokes undefined behavior. You may want to use sizeof(data), as data is an array.
In case you're not interested in sender's info, just pass a NULL as the corresponding argument.
To add to that, for connectionless sockets (UDP), it's actually required to get the sender information. For connection oriented sockets, you have another stripped-down alternative , recv() which only takes care of receiving and storing the data.
This DatagramPacket, however, only held the object in which to place the data.
And the source address and port, and the length of the data.
So, why does C's implementation of UDP receiving require that you know the sender's information?
It doesn't. It has the option to tell you the source address and port. It's a result parameter, not an input.
You compare different functions from Java and C.
In C there is also a recv() function that does not provide any address.
The sole puprpose of recvfrom over recv is to get the sender's address.
Normally servers reply to packets that they receive. Wihout an address that is not possible.
If you do not care about the sender of your packets, just take recv.
Or to put it the other way around:
If you don't care about the sender, why did you pick the recvfrom version of recv?
I wonder what does the server server if it doesn't care about the client's addresses... But that is not related to your question.
You could do it like these,
int sockfd_recv;
struct sockaddr_in recvaddr;
bzero(&recvaddr, sizeof(recvaddr));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(port_recv);
recvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sockfd_recv, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
Is there a way, as the server, to get the client's IP address as a string after receiving a message from the client with recvfrom()? I would assume it is in the sockaddr_in struct, but I don't know how to access it. Can anyone tell me how I can do this?
The IP address is indeed stored in the struct sockaddr or struct sockaddr_in whose address was passed to recvfrom, and (assuming the structure is named "sender") it can be converted to a string with:
#include <arpa/inet.h>
char* ipString = inet_ntoa(sender.sin_addr);
I'm using recvfrom in my program to get DGRAM data from a server I specify in src_addr. However, I'm not sure why I need to initialize and pass in addrlen.
I read the man page and I didn't really understand what it's getting at.
If src_addr is not NULL, and the underlying protocol provides the source address, this source address is filled in. When
src_addr is NULL, nothing is filled in; in this case, addrlen is not
used, and should also be NULL. The argument addrlen is a value-result argument, which the caller should initialize before the
call to the size of the buffer associated with src_addr, and
modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small;
in this case, addrlen will return a value greater than was supplied to the call.
I'm guessing that it's got something to do with src_addr being ipv4 or ipv6. Is this correct?
Thanks!
Maybe there is a missinterpretation from your side. Talking about:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr is not used to hand in the adress that you would like to listen to, but rather a storage location provided by you to get the actual source address handed out.
Thus if you set src_addr to NULL because youre not interested in the address at all, you don't have to care about addrlen as it won't get used anyway.
If on the other hand you want to be informed about the source address, you not only have to provide a storage location, but also tell how big the storage location you provided is.
Thats why you should initialize *addr_len to the buffer size you allocated.
After your call the value pointed to by addrlen will inform you about how much (if any) of the space you allocated to store the source address got actually filled with data.
About sizes
The whole hassle with struct sockaddr and passing sizes back and forth has to do with the fact that even thoug they're most heavily used in networking sockets were intended to be much more general concept.
Think about unix domain sockets as an an example as they are implemented via the filesystem they require an adressing scheme totaly different from that known from IP based networking. The type of sockaddr used here is:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
sun_path[UNIX_PATH_MAX]; /* pathname */
};
Compare this to the struct used in IP based networking:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
it should be clear both don't have too much in common.
sockets were designed to be able to fit both cases.
ssize_t recvfrom(int socket, void *buffer, size_t length, int flags,
struct sockaddr *address, socklen_t *address_len);`
The address_len argument specifies the length of the address structure i.e. the number of bytes to use from the start address indicated at address(start address of memory location + number of bytes from the start address that hold the value)
The structure is defined in /usr/include/bits/socket.h
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
Thus the sa_data field holds the address data (start address of the data) whose length is indicated by the address_len argument.
... whenever a function says it takes a struct sockaddr* you can cast your
struct sockaddr_in*, struct sockaddr_in6*, or struct sockadd_storage*
to that type with ease and safety.
Therefore, as indicated in the man page and #WhozCraig in the comment to your question, this field is updated with the actual size when the method returns.
More information
recvfrom
Beej's Guide to Network Programming - struct sockaddr and pals
I am writing a network program where, in the server part, I want to accept connections from multiple clients using a listening socket. So I declare an array of address structs like this:
struct sockaddr_in* client;
which I create using malloc and later on, to accept connections I type:
newsock = accept(fd_skt, (struct sockaddr *)&client[i], &(sizeof(client[i])));
and there I get "lvalue required as unary '&' operand" from the compiler. Can anyone figure out what I have done wrong?
Yes, you can't take the address of something that isn't an lvalue, that is an object with an address. The result of the sizeof operator is just a value, it isn't an object with an address.
You need to create a local variable so that you can take its address.
E.g.
socklen_t addrlen = sizeof client[i];
newsock = accept(fd_skt, (struct sockaddr *)&client[i], &addrlen));
As an aside, struct sockaddr_in* client; declares a pointer, not an array. To use client as an array you need to assign it to a dynamically allocated array at some point before the call to accept. I assume that this is what you are doing when you say "I create using malloc".
Alternatively you could actually declare client as an array.
struct sockaddr_in client[MAX_CLIENTS];
Charles' answer is correct, but one way to get around this kind of obnoxious function interface that requires a pointer to a value you plan to just throw away is to use compound literals:
newsock = accept(fd_skt, (struct sockaddr *)&client[i], (socklen_t[]){sizeof client[0]});
I'm learning socket programming in C & downloaded a simple tcp server source file. I understand every line except the 2nd parameters in these functions:
accept(socket_fd, (struct sockaddr *)&client, &length);
bind(socket_fd, (struct sockaddr *)&server, length);
The accept + bind functions are the typical functions in "sys/types.h" & "sys/socket.h", and the man page describes it as a pointer to a struct, but I still can't understand what's really going on here.
Can someone please explain what is going on in the second parameter? The brackets, pointer and address symbols are confusing me in the same expression.
Thanks in advance!
The & symbol essentially means "get the address of the value/object". The (struct sockaddr *) is a cast. It tells the compiler that you want to treat the address as a pointer to a sockaddr structure. So together, it is telling the compiler that client can be treated as a sockaddr structure and to pass the address of it to the function. In the case of the accept function, the address of the connecting socket will be stored in the given structure.
What's happening is that accept and bind function are expecting struct sockaddr pointers, and your client and server variables are probably declared as (struct sockaddr *). So, in order to avoid a warning in C or a compiler error in C++, you need an explicit cast which you do by putting the expression:
(struct sockaddr *)
Before your parameter.
And you need the ampersand, because client and server are not pointers. They were probably declared like:
struct sockaddr_in client, server;
It's also worth mentioning that the structures are closely related. Take a look at the picture from Stevens UnP.