I am using the select call and accepting the connection from "X" no of clients.
I made duplex connection i.e. server to client and client to server.
When connection is established between 2 entities ,I am going to send
data in chunks from one entity to other.
During send I read one file in chunks and send the data in chunks.
while(file_size !=0)
{
read_bytes = read(fd, buff, sizeof(buff));
cnt_ = send(_sock_fd,buff,actually_read,0);
file_size = file_size - cnt_;
printf("total sent remaining %d : %d\n",size,actually_read);
}
while at receiver side
//First I send the header which contain size it got accepted fine but during the following send call I used "get_readable_bytes" (Using ioctl) which returns me the no of bytes arrived at socket
`while(size != 0)
{
int test_ = 0;
while(((cnt_= get_readable_bytes(_sock_fd))== 0) )//&& test_ == 0
{
cnt_= get_n_readable_bytes(_sock_fd);
printf("Total bytes recved %d\n",cnt_);
//test_ = test_ + 1;
}
while(cnt_ != 0)
{
actually_read = recv(_sock_fd, buff, sizeof(buff),0);
int _cnt = get_n_readable_bytes(_sock_fd);
printf("Total bytes recved %d\n",cnt_-_cnt);
write(_fd,buff,actually_read);
cnt_ = cnt_ - actually_read;
test_ = 0;
}
`Now the problem is
1.During this execution of receive function control automatically go to the select function and it tries to execute whole receive function again so is there any way to synchronize the sender and receivers such that when the sender complete then start receiver or as soon as sender start receiver ?
2.And how do I maintain the count of bytes sent and received.
and this is my select call
`is_read_availble = select(maxfd + 1,&read_set,NULL,NULL,&timeout)`
with timeout 10sec.
Sketch of the kind of buffer code you need. (To allow partial reads/writes, the buffers need to be persistent between calls) BTW: you really need to handle the -1 return from read() and write() because they would seriously disturb your buffer-bookkeeping. EINTR + EAGAIN/EWOULDBLOCK is very common.
struct buff {
unsigned size;
unsigned bot;
unsigned top;
char *buff;
};
struct buff bf = {0,0,0,NULL};
initialisation:
bf.buff = malloc(SOME_SIZE);
/* ... error checking omitted */
bp.size = SOME_SIZE;
bp.bot = bp.top =0;
reading:
unsigned todo;
int rc;
/* (maybe) shift the buffer down to make place */
todo = bf.top - bf.bot;
if (todo) {
memmove (bf.buff, bf.buff + bf.bot, todo);
bf.top = todo; bf.bot = 0;
}
todo = bf.size - bf.top;
if (!todo) { /* maybe throttle? ... */ return; }
rc = read (fd, bf.buff+bp.top, todo);
/* ... error checking omitted */
if (rc == -1) switch (errno) {...}
else if (rc == 0) {...}
else {
total_read += rc;
bp.top += rc;
}
writing:
unsigned todo;
int rc;
todo = bf.top - bf.bot;
if (!todo) { /* maybe juggle fd_set ... */ return; }
rc = write (fd, bf.buff+bp.bot, todo);
/* ... error checking omitted */
if (rc == -1) switch (errno) {...}
else if (rc ==0) { ...}
else {
bp.bot += rc;
total_written += rc;
if (bp.bot == bp.top) bp.bot = bp.top =0;
}
/* ... this is the place to juggle the fd_set for writing */
Related
Summarize the Problem:
I wrote a userspace SPI driver in linux for the NRF24L01+ transceiver. My goal is to send files to a server. A jetson nano is the sender, and a raspberry pi 3b+ the receiver. Both the spi and nano are running Linux.
I can consistently send packets and receive acknowledgements.
However, the issue is whenever I send a packet such as 0x ff ee dd 00 cc bb aa the receiver only receives the packet 0x ff ee dd 00 00 00 00. So what is happening is that whenever the first byte encountered is zero, the rest of the packet becomes zero. This causes the files I send to become corrupted.
I was able to reproduce this bug with a char array having a similar pattern. I noticed this trend when I printed out the file contents I was sending on the transmitter and receiver.
What I've tried:
I've tried altering my SPI read function. What I thought was happening was the chip select line was being flipped high early. This did not work, I got the same results.
I've printed the packets before calling the ioctl() function from the transmitter and the packet remains intact.
I've printed the return value of the ioctl() function to see how many bytes I was receiving and sending. I was sending 31 bytes from the transmitter, and receiving 32 bytes from the receiver. So it doesn't look like my reads and sends are failing.
If I had a logic analyzer my next step would be to check the SPI pins on the transmitter, but unfortunately I don't have one.
I've added a 10uF decoupling capacitor on the transceivers and that sped up communication.
Show Some Code:
Receiver side:
/**
* Reads the payload when data pipe
* is available.
*
* spi_dev_fd: file descriptor for spi device.
* */
int nrf_rx_read(int spi_dev_fd, char * payload, int * pipe, int * bytes)
{
int pipe_temp, rtn;
// TODO: Add timeout.
do
{
rtn = nrf_rx_pipe_available(spi_dev_fd, &pipe_temp);
}while(rtn != 0);
if(rtn == 0)
{
char status;
if(bytes != NULL)
{
char size;
spi_read_msg(spi_dev_fd, R_RX_PL_WID, &status, &size, 1);
*bytes = (int) size;
}
spi_read_msg(spi_dev_fd, R_RX_PAYLOAD , &status, payload, (int) NUM_PAYLOAD_BYTES);
*pipe = pipe_temp;
char msg;
msg = RX_DR;
spi_send_msg(spi_dev_fd, W_REGISTER | STATUS, &msg, 1);
return 0;
}
return 1;
}
bool nrf_rx_pipe_available(int spi_dev_fd, int * pipe)
{
char addr = NOP;
char status;
spi_read_msg(spi_dev_fd, addr, &status, NULL, 0);
if((status & RX_DR) > 0)
{
*pipe = (status >> RX_P_NO) & 0x07;
if(*pipe > 5)
{
return 1;
}
return 0;
}
return 1;
}
int spi_read_msg(int spi_dev_fd, char addr, char * status, char * copy_to, int len)
{
char data_buffer;
char recv_buffer[len + 1];
struct spi_ioc_transfer xfer;
memset(&xfer, 0, sizeof(xfer));
memset(&recv_buffer, 0, sizeof(recv_buffer));
data_buffer = addr;
xfer.tx_buf = (unsigned long) &data_buffer;
xfer.rx_buf = (unsigned long) recv_buffer;
xfer.len = len + 2;
xfer.bits_per_word = 8;
xfer.speed_hz = 1000000;
xfer.cs_change = 0;
xfer.rx_nbits = len * 8;
xfer.tx_nbits = 8;
int res = ioctl(spi_dev_fd, SPI_IOC_MESSAGE(1), xfer);
if(res > 0)
{
status[0] = recv_buffer[0];
if(copy_to != NULL)
{
string temp = string(recv_buffer);
temp = temp.substr(1);
strncpy(copy_to, temp.c_str(), len);
}
// debug code
for(int i = 0; i < len; ++i)
{
printf("copy_to: %x \n ", copy_to[i]);
}
// end debug code.
}
return res;
}
Transmitter side:
/**
* Function to load a payload and send a packet.
*
*
* spi_dev_fd: file descriptor for spi device.
* */
int nrf_tx_send_packet(int spi_dev_fd, char * payload, int len)
{
int rtn;
// Put low so we can add the payload.
gpio_set_value((unsigned int) GPIO_CE, (unsigned int) GPIO_LVL_LOW);
// Set a new payload.
nrf_tx_new_payload(spi_dev_fd, payload, len);
// Start tx transmission.
gpio_set_value((unsigned int) GPIO_CE, (unsigned int) GPIO_LVL_HIGH);
do
{
rtn = nrf_tx_pending_send(spi_dev_fd);
if(rtn == 2)
{
char clr = MAX_RT;
spi_send_msg(spi_dev_fd, W_REGISTER | STATUS, &clr, 1);
}
}while(rtn != 1);
// Go back to standby mode
gpio_set_value((unsigned int) GPIO_CE, (unsigned int) GPIO_LVL_LOW); // Setting chip enable to 0.
char reg = W_REGISTER | STATUS;
char val = RX_DR | TX_DS | MAX_RT;
spi_send_msg(spi_dev_fd, reg, &val, 1);
return 0;
}
int spi_send_msg(int spi_dev_fd, char addr, char * data, int len)
{
char data_buffer[len + 1];
char recv_buffer;
struct spi_ioc_transfer xfer;
memset(&xfer, 0, sizeof(xfer));
memset(&recv_buffer, 0, sizeof(recv_buffer));
data_buffer[0] = addr;
for(int i = 1; i < len + 1; ++i)
{
data_buffer[i] = data[i-1];
printf("databuffer[i]: %x \n", data_buffer[i]);
}
xfer.tx_buf = (unsigned long) data_buffer;
xfer.rx_buf = (unsigned long) NULL;
xfer.len = len + 1;
xfer.bits_per_word = 8;
xfer.speed_hz = 1000000;
xfer.cs_change = 0;
//xfer.rx_nbits = 8;
xfer.rx_nbits = 0;
xfer.tx_nbits = (8 * len) + 8;
int res = ioctl(spi_dev_fd, SPI_IOC_MESSAGE(1), xfer);
printf("res: %i \n", res);
return res;
}
I tried to add all the relevant code, sorry if it is a bit much. Main thing to look at is the send and receive functions. They all work as expected until I encounter the zeroed out byte.
If I am missing any information that can help someone out please let me know and I can add it. I think the send and receive functions are the most important however. I'm able to set and read the registers of the transceiver.
I can send files now!
The fix was done in spi_read_msg() function.
The problem was I was converting the buffer received to a string, which caused the data to be trimmed when the byte 0x00 was encountered. This is also equivalent to the null terminating character.
Receiver code:
int spi_read_msg(int spi_dev_fd, char addr, char * status, char * copy_to, int len)
{
char data_buffer;
char recv_buffer[len + 1];
struct spi_ioc_transfer xfer;
memset(&xfer, 0, sizeof(xfer));
memset(&recv_buffer, 0, sizeof(recv_buffer));
data_buffer = addr;
xfer.tx_buf = (unsigned long) &data_buffer;
xfer.rx_buf = (unsigned long) recv_buffer;
xfer.len = len + 2;
xfer.bits_per_word = 8;
xfer.speed_hz = 1000000;
xfer.cs_change = 0;
xfer.rx_nbits = len * 8;
xfer.tx_nbits = 8;
int res = ioctl(spi_dev_fd, SPI_IOC_MESSAGE(1), xfer);
if(res > 0)
{
status[0] = recv_buffer[0];
if(copy_to != NULL)
{
for(int i = 0; i < len; ++i)
{
copy_to[i] = recv_buffer[i + 1];
}
}
}
return res;
}
I am implementing Go back N protocol in C.I keep a char buffer and append the header informationa and data and send it to the server from the client process.
When I keep window size less than 10, the sequence number takes the value 1-9and occupies only one byte in the buffer. When the window size is >= 10 , sequence number may 1 or 2 bytes in the character array. So I am appending length of sequence number also as a header information. That time I am getting some random data written in the output file at the server. Whereas for window size less than 10 I coded without adding sequence no. length info. Everything works fine that time.
The codes are quiet exhaustive. Yet,
Can anybody suggest any corrections to be made in the code?
client code for window size 10:-
#include<stdio.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<time.h>
#include<sys/time.h>
unsigned short int checksum_2(void *buffer,unsigned int len,unsigned short int seed)
{
unsigned char *buf1 = (unsigned char*)buffer;
int i;
for(i=0;i<len;i++)
{
seed+=(unsigned int)(*buf1++);
}
//fold 32-bit sum to 16 bit
while(seed>>16)
seed = (seed & 0xFFFF)+ (seed>>16);
return seed;
}
unsigned short int checksum(unsigned char * buff,unsigned int count)
{
register unsigned int sum = 0;
//main summing loop
while(count >1)
{
sum += *((unsigned short int*) buff);
(buff)++;
count = count -2;
}
//add left over byte if any
if(count>0)
{
sum += *((unsigned char *)buff);
}
//fold 32-bit sum to 16 bit
while(sum>>16)
sum = (sum & 0xFFFF)+ (sum>>16);
return (~sum);
}
char* rand_corrupt(unsigned char* buff)
{
int n = strlen(buff);
char* str_temp = (char*)calloc(1,n); //allocate memory for a string
int i;
int num;
num = rand() %100;
if(num>95)
{
for(i=0;i<strlen(buff);i++)
{
buff[i] = buff[i]+1;
}
}
strcpy(str_temp,buff);
return str_temp;
}
char *itoa(long i,char *s,int dummy_radix)
{
sprintf(s,"%ld",i);
return s;
}
void rand_wait()
{
int num;
int i;
num = rand() %100;
if(num<5)
{
for(i=0;i<10;i++)
{
sleep(1);
}
}
return;
}
int
main (int argc, char **argv)
{
int sock,length,n;
struct sockaddr_in server,from;
struct hostent *hp;
long int packets =0;
clock_t t; //to measure CPU time
struct timeval start,end; //to measure actual execution time
struct timeval timer; //for the timer implementation
double time_diff;//to measure the timer for the execution time of code
unsigned short int check;
void* buff;
printf("initializing 2 d array\n");
char buf[1024][1024]={0};
printf("initialisation failed\n");
int nread[1024] = {0,};
char new_buff[65535]={0, };
char ack_buf[65535]={0, };
char* temp;
char checksum_info[1024]={0, } ;
char bytes_info[1024] = {0, } ;
unsigned short int bytes_len;
char bytes_len_str[1024]={0, };
char seqnum[10] = {0,};
int seq_no;
char current;
char data_1[75535]={0, };
unsigned short int check_length;
char check_len_str[1024]={0, };
char ack_check_len[1024]={0, };
unsigned short int ack_checksum_len;
char ack_check[65535]={0, };
unsigned short int ack_checksum;
char temp_buf[65535]={0, };
char temp_buffer[655]={0,};
unsigned short int actual_checksum;
unsigned short int length1;
char content[65535]={0, };
int i,j;
char end_flag =0; //to mark the end of file or error occured while reading file
int total_len[1024]={0,};
char data[1024][6553]= {0,};
char *tmp = NULL;
int msec,trigger;
clock_t difference,initial;
char ack_seqlen_char[8]= {0,};
int ack_seqlen;
int ack_seq;
char ack_seq_char[10] ={0,};
int seqnum_len;
char seqlen_char[10] = {0,};
t = clock();
gettimeofday(&start,NULL);
buff = calloc(1,1024);//allocating memory for message buffer
if(buff == NULL)
{
printf("memory allocation failedi\n");
return 1;
}
temp = calloc(1,65535); //allocating memory for new message
if(temp == NULL)
{
printf("memory allocation failed\n");
return 1;
}
// checking if hostname and the port address is provided //
if(argc!=3)
{
printf("insufficient arguments\n");
exit(1);
}
//create a socket//
sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
printf("error in opening socket\n");
return 1;
}
//to get the hostname of the system or the machine//
hp= gethostbyname(argv[1]);
if(hp==0)
{
printf("Unknown host\n");
return 1;
}
//build the server's IP address //
bzero((char *)&server,sizeof(server));
bcopy((char*)hp->h_addr,(char *)&server.sin_addr,hp->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
length = sizeof(server);
/*open the file that we wish to transfer*/
FILE *fp = fopen("alice.txt","rb");
if(fp==NULL)
{
printf("file open error");
return 1;
}
fseek(fp,0,SEEK_END); //if exists read the size of the file
size_t file_size = ftell(fp);
fseek(fp,0,SEEK_SET);
printf("size of the file is %d\n", file_size);
/*find the number of packets*/
if(file_size == 0)
{
packets = 0;
}
else
{
packets = (file_size/1024)+1 ;
}
/*send the number of packets to the server*/
itoa(packets,(char*)buff,10);
printf("packets =%s\n",(char*)buff);
n= sendto(sock,buff,1024,0,(struct sockaddr *)&server,sizeof(struct sockaddr));
if(n<0)
{
printf("error in sending message to the server");
return 1;
}
int loop =1;
/*Read data from file and send it*/
int packetNum = 0;
int N = 10;
int base = 1;
int nextseqnum =1;
int sequence;
int seqnum_next = 1;
while(1)
{
while(nextseqnum<base+N) //send N consecutive packets
{
memset(buf[nextseqnum], 0, 65535); //clear the contents of buf before fread
memset(seqnum,0,10);
memset(seqlen_char,0,10);
memset(checksum_info,0,1024);
memset(check_len_str,0,1024);
memset(bytes_info,0,1024);
memset(bytes_len_str,0,1024);
memset(data[nextseqnum],0,6553);
/*First read file in chunks of 1024 bytes */
nread[nextseqnum] = fread(buf[nextseqnum],1,1024,fp);
printf("Bytes read %d\n",nread[nextseqnum]);
/*if read was success ,send data*/
if(nread[nextseqnum]>0)
{
printf ("I am HERE\n");
check = checksum(buf[nextseqnum],nread[nextseqnum]); //calculate the checksum
printf("checksum is %d\n",check);
itoa(check,checksum_info,10); //convert checksum into string
check_length = strlen(checksum_info); //calculate the length of the checksum
itoa(check_length,check_len_str,10); //convert the checksum lenght to string form
itoa(nread[nextseqnum],bytes_info,10); //the data length in string format
bytes_len = strlen(bytes_info);
itoa(bytes_len,bytes_len_str,10);
itoa(seqnum_next,seqnum,10); //combine seq no,checksum and the data content into one packet
printf("sequence number is %s\n",seqnum);
seqnum_len = strlen(seqnum); //getting the length of the sequence
itoa(seqnum_len,seqlen_char,10); //converting it into string format
strncat(data[nextseqnum],seqlen_char,strlen(seqlen_char));
strncat(data[nextseqnum],seqnum,strlen(seqnum));
strncat(data[nextseqnum],check_len_str,(strlen(check_len_str)));
strncat(data[nextseqnum],checksum_info,(strlen(checksum_info))); //adding checksum
strncat(data[nextseqnum],bytes_len_str,strlen(bytes_len_str)); //adding data length
strncat(data[nextseqnum],bytes_info,strlen(bytes_info)); //adding the data length info
j= seqnum_len+3+bytes_len+check_length;
for(i=0;i<nread[nextseqnum];i++)
{
data[nextseqnum][j] = buf[nextseqnum][i];
j++;
}
total_len[nextseqnum] = nread[nextseqnum]+3+bytes_len+check_length;//finding the total length
data[nextseqnum][total_len[nextseqnum]] = '\0';
n= sendto(sock,data[nextseqnum],total_len[nextseqnum],0,(struct sockaddr *)&server,sizeof(struct sockaddr));///send the data packet after adding the header info
if(n<0)
{
printf("error in sending message to the server");
fclose(fp);
return 1;
}
if(nextseqnum == base)
{
msec = 0;
trigger = 50; /*50 ms*/
initial = clock(); /*start the timer*/
}
nextseqnum++;
seqnum_next = nextseqnum%N;
if(seqnum_next == 0)
{
seqnum_next = N;
}
sleep(1);
}
/*There is something tricky going on with the read..
* Either there was error ,or we reached end of file.
*/
else
{
if(feof(fp))
printf("End of file\n");
if(ferror(fp))
printf("Error reading\n");
end_flag = 1; //set the flag that it has reached EOF or encountered error
break;
}
}
while(1) //wait for the acknowledgement for the sent packets till timeout , here timer value is set to 50 ms
{
difference = clock()-initial;
msec = difference*1000/CLOCKS_PER_SEC;
if(msec<trigger) //if it is not timeout then receive the ack packets
{
memset(ack_buf, 0, 65535); //clear the contents of ack_buf
memset(ack_check_len,0,1024);
memset(ack_check,0,65535);
memset(content,0,65535);
memset(temp_buf,0,65535);
//printf ("Receiving packet\n");
n = recvfrom(sock,ack_buf,1024,MSG_DONTWAIT,&server, &length); //receive the ack from the server
if(n>0)
{
j=0;
for(i=0;i<1;i++)
{
temp_buffer[j] = ack_buf[i];
j++;
}
temp_buffer[j] = '\0';
strcpy(ack_seqlen_char,temp_buffer);//get the length of the ack sequence //number
ack_seqlen = atoi(ack_seqlen_char); //converting it to int
j=0;
for(i=1;i<1+ack_seqlen;i++)
{
ack_seq_char[j]= ack_buf[i];
j++; //next bytes till i=ack_seqlen will be sequence number
}
seq_no = atoi(ack_seq_char);
printf("Ack received for the sequence number %d\n",seq_no);
j =0;
for(i=1+ack_seqlen;i<2+ack_seqlen;i++) //next byte will have the length of checksum
{
temp_buf[j] = ack_buf[i]; //extracting the checksum length
j++;
}
temp_buf[j] = '\0'; //ending the string with a null character
strcpy(ack_check_len,temp_buf); //length of checksum will be in string format
ack_checksum_len = atoi(ack_check_len); //convert it into int
j = 0;
for(i=2+ack_seqlen;i<(2+ack_seqlen+ack_checksum_len);i++) //extract the checksum
{
ack_check[j] = ack_buf[i];
j++;
}
ack_check[j] ='\0';
ack_checksum = atoi(ack_check); //the checksum will be in string format,convert it into the integer format
j=0; //extract the content i.e the actual ack message
while(i<n)
{
content[j] = ack_buf[i];
i++;
j++;
}
content[j] ='\0';
length1 = strlen(content);
actual_checksum = checksum(content,length1);//calculate the actual checksum
printf("checksum of the ack received is %d\t and the checksum of the ack sent is %d\n",actual_checksum,ack_checksum);
if(actual_checksum == ack_checksum) //if the ack is not corrupt then update the base
{
printf("successfully recived packet %d\n",seq_no);
base =seq_no+1;
if(base == nextseqnum) //go to sending next series of packets
{
printf ("base is %d\n", base);
break;
}
else
{
initial = clock(); //start timer for next in-flight packet
}
}
else //if it is a corrupt ack
{
printf("Ack corrupted,wait till timeout\n");
}
} else {
if (end_flag == 1) {
printf ("Reached END\n");
break;
}
}
}
else //if the timer is out, resend the packet from the packet with seqno
// base till nextseqnum-1,start timer
{
initial = clock();
sequence = base;
while(sequence <= nextseqnum-1)
{
n= sendto(sock,data[sequence],total_len[sequence],0,(struct sockaddr *)&server,sizeof(struct sockaddr));///send the data packet after adding the header info
if(n<0)
{
printf("error in sending message to the server");
fclose(fp);
return 1;
}
sleep(0.5);
}
}
}
if(end_flag == 1) //if there had been an error in reading file of EOF had reached break out of
// the outermost while loop
{
printf ("I am at the END\n");
break;
}
}
printf ("Sending finish packet\n");
strcpy(buff,"Finish");
n= sendto(sock,buff,1024,0,(struct sockaddr *)&server,sizeof(struct sockaddr));
if(n<0)
{
printf("error in sending message to the server");
return 1;
}
fclose(fp); //close the file to complete the transmission
t = clock()-t;
gettimeofday(&end,NULL);
double time_taken = ((double)t)/CLOCKS_PER_SEC; //in secconds
printf(" The CPU time for transmission %f seconds \n",time_taken);
double delta = ((end.tv_sec - start.tv_sec)*1000000u+ end.tv_usec - start.tv_usec)/1.e6;
printf("The actual execution time took %f seconds\n", delta);
close(sock); //close api tries to complete the transmission if there is data waiting to be transmitted
return 0;
}
I'm reading "Unix Networking Programming" the 3ed edition.
I encounter a question in 6.8 section "TCP Echo Server (Revisited)", here present code as below:
#include "unp.h"
int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
maxfd = listenfd; /* initialize */
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients");
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue;
**if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
/*4connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);**
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
About this program, Author said the server will suffer from DDOS attack as below:
enter image description here
the point is once a client request come, server read entire line then echo it. But is this code, we see server use Read function read data from client, not ReadLine or Readn, latters won't return until encounter a '\n' or fetch specified size data, but Read function immediately return in this case.
Read function just wrapper of system call "read" as below:
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ( (n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return(n);
}
So I confused why this server will suffer from a ddos attack?
Anyone can clarify it? Thank you very much!
I think that the confusion is due to a possible difference between the second edition and third edition of the book.
I have the 2nd edition and in it the "Read" is actually a "Readline". Then the explanation makes sense, due to the fact that Readline insists on reading until a newline.
I do not have a copy of the 3rd edition to compare with.
As for the explanation from Drunken Code Monkey, true, the read is blocking, however it is protected by the select which will guarantee that the read is only called if there is activity on the socket (either a disconnect, or at least 1 byte to read). So it is guaranteed that the read will not block. But see my explanation regarding if Read is replaced with Readline (as in 2nd edition)
See also a previous post on Stack Overflow Unix Network Programming Clarification
As per Stephane's response, here is an example to illustrate proper connection handling in a threaded TCP server. Note that I am not comfortable enough with linux development to write it easily, so this is C#, but the program flow should be the same. Treat it as pseudo-code if you must.
// We use a wait handle here to synchronize the client threads with the main thread.
private static AutoResetEvent _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
// Start the server on port 1337
StartServer(1337);
}
private static void StartServer(int port)
{
// Create a connection listener
var listener = new TcpListener(IPAddress.Any, port);
try
{
// Start the listener
listener.Start();
while (true)
{
// Wait for a connection, and defer connection handling asynchronously.
listener.BeginAcceptTcpClient(new AsyncCallback(HandleAsyncConnection), listener);
_waitHandle.WaitOne();
_waitHandle.Reset();
}
}
catch (SocketException ex)
{
// Handle socket errors or any other exception you deem necessary here
}
finally
{
// Stop the server.
listener.Stop();
}
}
private static void HandleAsyncConnection(IAsyncResult state)
{
// Get the listener and the client references
var listener = (TcpListener)state.AsyncState;
using (var tcpClient = listener.EndAcceptTcpClient(state))
{
// Signal the main thread that we have started handling this request.
// At this point the server is ready to handle another connection, and no amount
// of tomfoolery on the client's side will prevent this.
_waitHandle.Set();
// Declare buffers
var inBuff = new byte[tcpClient.ReceiveBufferSize];
var outBuff = new byte[tcpClient.SendBufferSize];
// Get the connection stream
using (var stream = tcpClient.GetStream())
{
try
{
// Read some data into inBuff
stream.Read(inBuff, 0, tcpClient.ReceiveBufferSize);
// Do something with the data here, put response in outBuff...
// Send response to client
stream.Write(outBuff, 0, outBuff.Length);
}
catch (SocketException ex)
{
// Handle socket errors or any other exception you deem necessary here
}
}
}
}
I am stuck with my UDP talker app.
The goal for the moment is to initialize the server, register a client and then proceed to send something to that client.
I've worked my way through Beej's network guide and coded the following library implementation:
This inizializes the server
int init_udp_server(const char *port_string){
/** Check the input data **/
if(port_string == NULL)
port_string = DEFAULT_PORT;
/** Get the information for the server **/
memset(&addrinfo_hints, 0, sizeof addrinfo_hints);
/* Use either protocol (v4, v6) */
addrinfo_hints.ai_family = AF_UNSPEC;
/* Use UDP socket type */
addrinfo_hints.ai_socktype = SOCK_DGRAM;
/* Use system IP */
addrinfo_hints.ai_flags = AI_PASSIVE;
if( (ret = getaddrinfo(NULL, port_string, &addrinfo_hints, &addrinfo_server))
!= 0 ){
printf("Server:getaddrinfo: %s\n", gai_strerror(ret));
return -1;
}
/** Loop through the list returned by getaddrinfo and get socket **/
for( addrinfo_queue = addrinfo_server; addrinfo_queue != NULL;
addrinfo_queue = addrinfo_queue->ai_next){
if((sockfd = socket(addrinfo_queue->ai_family,
addrinfo_queue->ai_socktype, addrinfo_queue->ai_protocol)) == -1){
error("Server: get socket failed");
continue;
}
if(bind(sockfd, addrinfo_queue->ai_addr, addrinfo_queue->ai_addrlen)
== -1){
close(sockfd);
error("Server: Bind to socket error");
continue;
}
break;
}
/* If we got to addrinfo_queue == NULL, we did not get a valid socket */
if(addrinfo_queue == NULL){
error("Server: Could not bind a socket");
return -1;
}
/* We do not need the addrinfo_server anymore */
freeaddrinfo(addrinfo_server);
return 0;
}
This registers the client
int udp_server_setup_client(const char *client_addr, const char *port_string, int client_nr){
/** Check the input data **/
if(port_string == NULL)
port_string = DEFAULT_PORT;
if(client_addr == NULL){
error("No valid client list");
return -1;
}
if(client_nr < 0 || client_nr > 7){
error("No valid client Nr.");
return -1;
}
memset(&addrinfo_hints, 0, sizeof addrinfo_hints);
/* Use either protocol (v4, v6) */
addrinfo_hints.ai_family = AF_UNSPEC;
/* Use UDP socket type */
addrinfo_hints.ai_socktype = SOCK_DGRAM;
/* Get the information for the client */
if( (ret = getaddrinfo( client_addr, port_string, &addrinfo_hints,
¤t)) != 0 ){
printf("Client:getaddrinfo: %s\n", gai_strerror(ret));
return -1;
}
else{
/* We read out the IP, kind of a nice check to see wheter all went fine */
char ip4[INET_ADDRSTRLEN];
struct sockaddr_in *sa = (struct sockaddr_in*) current->ai_addr;
inet_ntop(AF_INET, &(sa->sin_addr),ip4, INET_ADDRSTRLEN);
printf("Clients address: %s\n",ip4);
addrinfo_clients[client_nr] = current;
}
return 0;
}
And finally this is for writing
int udp_server_write(const char *buffer, int buffer_size, int client_nr){
/* Sanity check of the input */
if(client_nr > (MAX_NR_CLIENTS - 1) || client_nr < 0){
error("Not a valid client");
return -1;
}
if(buffer == NULL){
error("Not a valid buffer address");
return -1;
}
/* Just so we type less */
current = addrinfo_clients[client_nr];
socklen = sizeof current->ai_addr;
if((ret = sendto(sockfd, (void*)buffer, buffer_size, 0,
(sockaddr*)current->ai_addr, socklen)) == -1){
printf("Failed to send message to client %i\n", client_nr);
printf("Error Code: %s\n",gai_strerror(ret));
return -1;
}
else if(ret < buffer_size){
printf("Wrote only %i of %i bytes\n", ret, buffer_size);
return -1;
}
return ret;
}
I call the functions like this
init_udp_server("3334");
udp_server_setup_client("192.168.1.5", "3334", 0);
udp_server_write(send_buf, 256, 0);
As soon as sendto() is called I get an error:
Failed to send message to client 0
Error Code: Bad value for ai_flags
I checked it with gdb and found that the addrinfo struct is filled correctly, and the address of the client is valid.
Any one an idea where to look? I am running out of ideas...
thanks, wenzlern
When calling sendto(), the last parameter is being set to sizeof current->ai_addr, which is wrong. current->ai_addr is defined as a sockaddr* pointer, so sizeof current->ai_addr will always return 4 on a 32-bit system and 8 on a 64-bit system. It just happens that IPv4 addresses are 4 bytes in size, so sizeof current->ai_addr will only work for IPv4 addresses on 32-bit systems, but will always fail for IPv6 addresses on 32-bit systems and all addresses on 64-bit systems. You need to use current->ai_addrlen instead of sizeof.
Also, passing -1 to gai_strerror() is not valid. It expects you to pass in a real error code, such as the return value of getaddrinfo() and getnameinfo(). sendto() does not return an actual error code. When it fails, you have to use WSAGetLastError() on Windows or errno on other systems to get the actual error code.
Try this:
if ((ret = sendto(sockfd, (char*)buffer, buffer_size, 0, (sockaddr*)current->ai_addr, current->ai_addrlen)) == -1)
{
#ifdef _WIN32
ret = WSAGetLastError();
#else
ret = errno;
#endif
printf("Failed to send message to client %i\n", client_nr);
printf("Error Code: (%d) %s\n", ret, gai_strerror(ret));
return -1;
}
Hello I've written the following code which is a part of a project. It is used to find the ESSID of the current associated network.
But it has a flaw: it also the displays the ESSID of the network with which I am not associated i.e. if I try to associate myself with a wireless n/w and if it is unsuccessful i.e. NO DHCP OFFERS ARE RECEIVED, then also it will display the that ESSID with which I have made my attempt.
Could anyone give me an ioctl call to find the BSSID of current associated wireless n/w?. In my opinion it is the only way with which I a can mark b/w associated and non associated.
CODE:-
int main (void)
{
int errno;
struct iwreq wreq;
CStdString result = "None";
int sockfd;
char * id;
char ESSID[100];
memset(&wreq, 0, sizeof(struct iwreq));
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
fprintf(stderr, "Cannot open socket \n");
fprintf(stderr, "errno = %d \n", errno);
fprintf(stderr, "Error description is : %s\n",strerror(errno));
return result ;
}
CLog::Log(LOGINFO,"Socket opened successfully");
FILE* fp = fopen("/proc/net/dev", "r");
if (!fp)
{
// TBD: Error
return result;
}
char* line = NULL;
size_t linel = 0;
int n;
char* p;
int linenum = 0;
while (getdelim(&line, &linel, '\n', fp) > 0)
{
// skip first two lines
if (linenum++ < 2)
continue;
p = line;
while (isspace(*p))
++p;
n = strcspn(p, ": \t");
p[n] = 0;
strcpy(wreq.ifr_name, p);
id = new char[IW_ESSID_MAX_SIZE+100];
wreq.u.essid.pointer = id;
wreq.u.essid.length = 100;
if ( ioctl(sockfd,SIOCGIWESSID, &wreq) == -1 ) {
continue;
}
else
{
strcpy(ESSID,id);
return ESSID;
}
free(id);
}
free(line);
fclose(fp);
return result;
}