Based from the answers I got from this thread, I've created this:
//Server
sock_init(); //from SFL, see http://legacy.imatix.com/html/sfl/
timeout = 50000;
serv_sock_input[0] = TCP(1234);
serv_sock_input[1] = UDP(9876);
input_protocols[0] = "tcp";
input_protocols[1] = "udp";
while (1)
{
FD_ZERO(&sock_set);
for (x = 0; x<number_of_inputs; x++)
{
FD_SET(serv_sock_input[x], &sock_set);
}
select_timeout.tv_sec = timeout;
select_timeout.tv_usec = 0;
if (select(0, &sock_set, NULL, NULL, &select_timeout) == 0)
printf("No requests");
else
{
for (x = 0; x<number_of_inputs; x++)
{
if (FD_ISSET(serv_sock_input[x],&sock_set))
{
printf("\nRequest on port %d: \n", x);
if ((strcmp(input_protocols[x],"tcp")) == 0) //in this case, 0 returned == TRUE
{
accept_socket(serv_sock_input[x]);
printf("Input TCP Port %d\n",x);
close_socket(serv_sock_input[x]);
}
else
{
printf("Input UDP Port %d\n",x);
}
}
}
}
}
sock_term();
}
int TCP (unsigned short port)
{
int sock;
struct sockaddr_in servAddr;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
exit(1);
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
exit(1);
if (listen(sock, 5) < 0)
exit(1);
return sock;
}
int UDP (unsigned short port)
{
int sock; /* socket to create */
struct sockaddr_in servAddr; /* Local address */
/* Create socket for sending/receiving datagrams */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
exit(1);
/* Construct local address structure */
memset(&servAddr, 0, sizeof(servAddr)); /* Zero out structure */
servAddr.sin_family = AF_INET; /* Internet address family */
servAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
servAddr.sin_port = htons(port); /* Local port */
/* Bind to the local address */
if (bind(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
exit(1);
return sock;
}
//Client
sock_init();
if ((client_sock_output = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
exit(1);
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
client_addr.sin_port = htons(1234);
if (connect(client_sock_output, (struct sockaddr *) &client_addr, sizeof(client_addr)) < 0)
exit(1);
closesocket(client_sock_output);
sock_term();
When the server starts, the server gets blocked at the if(select(...)) statement.
So when I run the Server, and then the client, the client connects to the server (sometimes it takes a couple times to run the client before they connect). Then the if(select...)) statement is no longer true and it proceeds to the else.
After that, the client closes the connection, and the program. However, and this is where my problem happens, the if(select(...)) statement is always false. I get this output:
Request on port 0:
Input TCP Port 0
Request on port 1:
Input UDP Port 1
This output repeats forever. How come it doesn't get stuck at the if(select(...))?
You have two problems: you don't understand how accept() works in TCP, and you need to read the incoming data in UDP.
select() tells you that a listening socket has connection to accept, or reading socket has data to read.
For select to stop telling you this, you need to actually read the data or accept the connection.
In your UDP branch, you need to call receiv to actually get the data. If you don't, select will keep telling you that you have data.
In your TCP branch, you call accept_socket. I don't know what is your implementation of it, but it's most probably wrong to close the socket you just called accept() on. accept() returns a new socket for you - the one you should be using for IO. If anything needs to be closed, it's that new socket.
Please check why you have this in server.
if (select(0, &sock_set, NULL, NULL, &select_timeout) == 0)
replace it with
if (select(maxDescPlus1, &sock_set, NULL, NULL, &select_timeout) == 0)
where maxDescPlus1 --> is number of descriptors to select plus 1 value.
Related
I am writing a C client server program using SCTP, so that at begin there is only one socket opened on client side for send and recv which is used by all threads. After a certain condition I have to open a new socket, now I have two of them and at this point both socket should be used for sending and receiving on a round-robin (alternate) fashion by all threads for load sharing. Similarly, as the no of sockets increase, it should be used by client alternative for load sharing.
is there a suggestion to achieve this? Using select, poll, normal sockets etc?
connSock = socket (AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (connSock == -1)
{
perror("socket()");
exit(1);
}
struct sctp_paddrparams params;
len = sizeof(params);
memset(¶ms, 0, sizeof(params));
if (getsockopt(connSock, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms, &len)) {
perror("getsockopt");
exit(1);
}
// set client address
struct sockaddr_in localaddr;
localaddr.sin_family = AF_INET;
char* client_ip = get_our_ip();
localaddr.sin_addr.s_addr = inet_addr(client_ip) ;
localaddr.sin_port = 0;
bind(connSock, (struct sockaddr *)&localaddr, sizeof(localaddr));
// set server address
bzero ((void *) &servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons (port);
servaddr.sin_addr.s_addr = inet_addr(server_ip);
ret = connect(connSock, (struct sockaddr *) &servaddr, sizeof (servaddr));
if (ret == -1)
{
perror("connect()");
close(connSock);
exit(1);
}
// ----> at this point only one socket is opened from client and all threads are using the same.
if(due_to_some_condition_got_new_server_ip){
// --> I have opened a new socket to connect to server_ip2. Now we
// have 2 sockets opened, hence all threads should use sockets alternatively to send and receive data
}
I'm writing a 2D RPG game in pure ANSI C, and I've decided to implement multiplayer, so I've written boilerplate POSIX code for the client and server, and implemented a login packet. The login packet works fine, however with the addition of a logout packet when the client quits, the server never receives it.
I've stepped through in a debugger and when the write call is executed on the client socket the file descriptor is zero. I also set a memory watchpoint on the variable holding the client socket file descriptor, and it was never touched. I'm not sure whether this is a C bug, or something in networking I'm missing so I'm looking for a pointer of how to go about fixing it.
Creating client socket:
struct sockaddr_in sockaddr;
client = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client < 0) {
perror("");
exit(EXIT_FAILURE);
}
memset(&sockaddr, 0x00, sizeof(struct sockaddr_in));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
inet_pton(AF_INET, server, &sockaddr.sin_addr);
if (connect(client, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0) {
perror("");
exit(EXIT_FAILURE);
}
Creating server socket:
struct sockaddr_in sockaddr;
server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server < 0) {
perror("");
exit(EXIT_FAILURE);
}
memset(&sockaddr, 0x00, sizeof(struct sockaddr_in));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr_in)) < 0) {
perror("");
exit(EXIT_FAILURE);
}
int flags = fcntl(server, F_GETFL, 0);
flags &= ~O_NONBLOCK;
if (fcntl(server, F_SETFL, flags) < 0) {
perror("");
}
Listening on server socket:
if (listen(server, maxClients) < 0) {
perror("");
close(server);
exit(EXIT_FAILURE);
}
...
Main loop (threaded for future offline support):
void *mainLoop(void *argv) {
void (*packetHandler)(void) = argv;
for (;;) {
serverClient = accept(server, NULL, NULL);
printf("%d\n", serverClient);
if (serverClient < 0) {
exit(EXIT_FAILURE);
}
(*packetHandler)();
#ifdef SHUTDOWN
if (shutdown(serverClient, SHUT_RDWR) < 0) {
close(serverClient);
close(server);
exit(EXIT_FAILURE);
}
#endif
}
return NULL;
}
Writing to sockets is done using standard read and write calls.
Note: The #ifdef block with SHUTDOWN is normally not compiled.
I have a Linux C application that must use UDP. The server broadcasts a "discovery packet" and then listens for any connected clients to answer with a similar echo. By using ports, the clients and server can then communicate using their different ports.
Here is how the server broadcasts its discovery packet:
int main() {
puts("starting");
int sock;
int yes = 1;
struct sockaddr_in broadcast_addr;
int addr_len;
int count;
int ret;
fd_set readfd;
char buffer[1024];
char outbound_buffer[63];
int i;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("sock error");
return -1;
}
ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));
if (ret == -1) {
perror("setsockopt error");
return 0;
}
memset(outbound_buffer,0,sizeof(outbound_buffer));
addr_len = sizeof(struct sockaddr_in);
memset((void*)&broadcast_addr, 0, addr_len);
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
broadcast_addr.sin_port = htons(PORT);
outbound_buffer[0] = 0xEF;
outbound_buffer[1] = 0xFE;
outbound_buffer[2] = 0x02;
ret = sendto(sock, outbound_buffer, 63, 0, (struct sockaddr*) &broadcast_addr, addr_len);
This works fine; the client receives the discovery and gets the server's IP and port:
int main() {
stoplink = 0;
stopData = 0;
int addr_len;
int count;
int ret;
fd_set readfd;
char buffer[1024];
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("sock error\n");
return -1;
}
addr_len = sizeof(struct sockaddr_in);
memset((void*)&server_addr, 0, addr_len);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(PORT);
ret = bind(sock, (struct sockaddr*)&server_addr, addr_len);
if (ret < 0) {
perror("bind error\n");
return -1;
}
while (1) {
puts("Initialized; await discovery");
FD_ZERO(&readfd);
FD_SET(sock, &readfd);
ret = select(sock+1, &readfd, NULL, NULL, 0);
if (ret > 0) {
if (FD_ISSET(sock, &readfd)) {
count = recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&client_addr, &addr_len);
if((buffer[0] & 0xFF) == 0xEF && (buffer[1] & 0xFF) == 0xFE) {
fprintf(stderr,"discovery packet detected\n");
cmdport = ntohs(client_addr.sin_port);
printf("\nClient connection information:\n\t IP: %s, Port: %d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
count = sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr*)&client_addr,
sizeof(client_addr));
}
}
}
puts("Now starting command processing loop");
This also works fine... but it sends the reply to the port on the server from which the broadcast came - this was randomly selected automatically by the server; so how do I know what port to listen to on the server side to receive the client's reply?
The server doesn't need to do anything additional. Once it sends a packet, the port on the server side is set.
For example, if the client sees that the server message came from port 34567, then the server socket is using port 34567. Then any messages sent to the server at port 34567 can be read by the same server socket.
So the server can just call recvfrom and it will get the response from the client.
You've swapped the normal meanings of the words "client" and "server" -- normally the server will bind to a specific port and listen for (broadcast) packets, while the client will broadcast a discovery packet to find the server. When the server receives a broadcast, it will reply to the client, which will just do a recv on it's (single) socket to get the reply, which will have the server's IP address. If there might be multiple servers, then they'll all reply to the client, so the client will see multiple replies and need to choose from them. But, most importantly, the client never needs to actually know which port it is using -- it just lets the system choose an otherwise unused port for it.
Thanks for all the input. getsockname() is what I needed.
I'm writting a simple socket server/client app.
I run into interesting problem. In my server code I call accept on non-blocking socket like this
while ((res = accept(m_sd, NULL, 0)) >= 0) { // There are new clients
... // Saving res as fd etc
}
Everything works perfectly - when there is a client, accept returns a valid file descriptor. However when a first client disconnects and second client connect, accept returns 0 - which is a valid FD, howerver all operation on this descriptor fails. This happens also for the next clients - accept is returning 0. After random number of clients, acceptr returns a "valid" (non-zero) descritpor, and than it repeats.
Note: When there are no clients, accept returns -1 as expected with errno EAGAIN - which is completly fine. When accept returns zero, errno is not set.
What could cause such a weird behavior?
Here's how I create server socket:
struct sockaddr_in serv_addr;
m_sd = socket(AF_INET, SOCK_STREAM, 0);
if (m_sd < 0){}
//Handle error
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
int optval = 1;
setsockopt(m_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
if (bind(m_sd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
// Handle error
}
fcntl(m_sd, F_SETFL, O_NDELAY); // Make socket non-blocking
listen(m_sd, 50);
And here's how I create client:
int rc;
struct sockaddr_in serveraddr;
struct hostent *hostp;
m_sd = socket(AF_INET, SOCK_STREAM, 0);
if (m_sd < 0)
// Handle error
memset(&serveraddr, 0, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
hostp = gethostbyname(hostname.c_str());
if (hostp == NULL)
// Handle error
memcpy(&serveraddr.sin_addr, hostp->h_addr, sizeof(serveraddr.sin_addr));
// connect to serveraddr
rc = connect(m_sd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
if (rc < 0)
//Handle error
//set to nonblocking
fcntl(m_sd, F_SETFL, fcntl(m_sd, F_GETFL, 0) | O_NONBLOCK);
This is the code, where I wait for new data from any client:
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(timeout).count();
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(m_sd, &rfds);
int end = m_sd;
for (const auto& s : m_clients) {
end = std::max(end, s.second.m_sd);
FD_SET(s.second.m_sd, &rfds);
}
int retval = select(end + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
// Error handling
}
return retval > 0; // There is pending data from client
Problem solved! I was accidentally closing fd 0 in my code, which caused this weird behaviour. Now everything works. Thanks for helping - you've showed me the right way
I need my server to broadcast a message (not that it matters but it contains its IP/port info). What I have currently is the working server broadcast, code below. I'm not sure about setting up the client because usually I would use the IP/port of the server which the client doesn't have until it receives the broadcast. The client never receives anything. Can someone tell me what is wrong.
Server:
struct sockaddr_in server, bcast;
int sockfd;
int blen = sizeof(bcast);
int svrlen = sizeof(server);
char buf[BUFLEN];
if((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){
printf("Socket error.\n");
exit(1);
}
int broadcastPermission = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (void *)&broadcastPermission,sizeof(broadcastPermission)) < 0){
printf("Error setting socket options.");
}
memset(&bcast, 0, sizeof(bcast));
bcast.sin_family = AF_INET;
bcast.sin_port = htons(PORT);
bcast.sin_addr.s_addr = htonl(INADDR_ANY);
string bcastIP = BCASTIP;
if(inet_aton("255.255.255.255", &bcast.sin_addr) == 0){
printf("Broadcast Address error.");
exit(1);
}
if (bind(sockfd, (struct sockaddr*)&server, sizeof(server)) == -1){
printf("Port error.\n");
exit(1);
}
fflush(stdout);
if(int bytes = sendto(sockfd, ipinfo, sizeof(ipinfo), 0, (struct sockaddr*)&bcast, blen) == -1){
printf("Broadcast send error.");
}
else{
printf("Sent"):
}
Client:
struct sockaddr_in server;
int sockfd;
int bytes;
int svrlen = sizeof(server);
char buf[BUFLEN]
if((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){
printf("Socket error.\n");
exit(1);
}
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(BPORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
while(1){
printf("Waiting for broadcast...\n\n");
fflush(stdout);
memset(buf,0,BUFLEN);
bytes = recvfrom(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&server, (socklen_t*)&svrlen);
printf("Received");
}
Your client is not calling bind() on the socket before trying to receive data.
http://cs.baylor.edu/~donahoo/practical/CSockets/code/BroadcastReceiver.c shows the following example which you may find helpful:
void DieWithError(char *errorMessage); /* External error handling function */
int main(int argc, char *argv[])
{
int sock; /* Socket */
struct sockaddr_in broadcastAddr; /* Broadcast Address */
unsigned short broadcastPort; /* Port */
char recvString[MAXRECVSTRING+1]; /* Buffer for received string */
int recvStringLen; /* Length of received string */
if (argc != 2) /* Test for correct number of arguments */
{
fprintf(stderr,"Usage: %s <Broadcast Port>\n", argv[0]);
exit(1);
}
broadcastPort = atoi(argv[1]); /* First arg: broadcast port */
/* Create a best-effort datagram socket using UDP */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
DieWithError("socket() failed");
/* Construct bind structure */
memset(&broadcastAddr, 0, sizeof(broadcastAddr)); /* Zero out structure */
broadcastAddr.sin_family = AF_INET; /* Internet address family */
broadcastAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
broadcastAddr.sin_port = htons(broadcastPort); /* Broadcast port */
/* Bind to the broadcast port */
if (bind(sock, (struct sockaddr *) &broadcastAddr, sizeof(broadcastAddr)) < 0)
DieWithError("bind() failed");
/* Receive a single datagram from the server */
if ((recvStringLen = recvfrom(sock, recvString, MAXRECVSTRING, 0, NULL, 0)) < 0)
DieWithError("recvfrom() failed");
recvString[recvStringLen] = '\0';
printf("Received: %s\n", recvString); /* Print the received string */
close(sock);
exit(0);
}
I need my server to broadcast a message (not that it matters but it contains its IP/port info).
That sounds a lot like service discovery. You should really use the standard mDNS/Zeroconf protocol for that. You can use the Avahi library for that (or use the Avahi service on Linux or Zeroconf on MacOS X).