I'm writing some server/client program in C Windows. I don't know if I'm sending and receiving buffers the right way, on google I only see people error checking it but not checking if the send() function sent less bytes then expected. This is an example from my project:
Client:
// send buffer size
uint32_t num = htonl(sizeBuffer);
char* converted_num = (char*)#
res = send(ClientSocket, converted_num, sizeof(uint32_t), 0);
if (res == SOCKET_ERROR)
{
printf("error send\n");
}
// send buffer
while (totalSent < sizeBuffer)
{
sent = send(ClientSocket, totalBuffer, sizeBuffer, 0);
totalSent += sent;
printf("sent: %d\n", sent);
printf("totalSent: %d\n", totalSent);
}
Server:
// recv buffer size
char b[sizeof(uint32_t)];
r = recv(s, b, sizeof(uint32_t), 0);
if (r == SOCKET_ERROR)
{
printf("error recv\n");
}
uint32_t sizeBuffer = ntohl_ch(&b[0]);
// recv buffer
while (totalReceived < sizeBuffer)
{
received = recv(s, buffer, sizeBuffer, 0);
strcat(totalBuffer, buffer);
bzero(buffer, 18384);
totalReceived += received;
printf("received: %d\n", received);
printf("totalReceived: %d\n", totalReceived);
}
printf("%s", totalBuffer);
The reason I use strcat() is because when I use printf() inside the while() loop it gets printed weirdly, like the previous buffer gets printed and the new buffer gets printed on top. I don't know why it behaves like this.
Is this the right way to send and receive buffers? And do I also have to check whether the size (num) of the buffer is send correctly, like how I send the buffer itself? If yes, how can I do that?
Related
I write program and it works fine, but i want to rewrite it using sendfile() and now i got stuck in a loop.
Server side:
send name = ok
send md5 checksum = ok
send size = ok
send file = ko
Client side:
recv name = ok
recv md5 cecksum = ok
recv size = ok
create dir and create file = ok
write data to created file = ko
P.S In previous version of program i stuck some time to, but it depend how much i use printf why? for e.x i add one line with printf program stuck, delete it, works fine.
UPDT: rewrite code client/server
client
/* Received file name */
int rc_byte = 0;
rc_byte = recv(fd, rx_tx_file->out_name, sizeof(rx_tx_file->out_name),0);
if (rc_byte < 0){
perror("Failed to receive file name: ");
exit(-1);
} else
printf("Recv out name %s\n", rx_tx_file->out_name);
//printf("file name rc %s\n", rx_tx_file->out_name);
trimm_path_name(rx_tx_file);
/* Received md5sum */
rc_byte = recv(fd, rx_tx_file->md5sum, sizeof(rx_tx_file->md5sum), 0);
if (rc_byte < 0) {
perror("Failed to receive check sum: ");
exit(-1);
} else
printf("recv md5s %s\n", rx_tx_file->md5sum);
/* Received file size */
rc_byte = recv(fd, &size, sizeof(size), 0);
if(rc_byte < 0) {
perror("Recevid size of file: ");
exit(-1);
}
printf("%d recv size\n", size);
to_read = size;
if (stat(dir, &st) == -1){
mkdir(dir, 0777);
}
send_data: (add func to server)
void send_data(int client_fd, m_file *rx_tx_file, int option, int size) {
int send_byte = 0;
int total_send = 0;
if (option == SEND_NAME) {
while (total_send < strlen(rx_tx_file->in_name)) {
send_byte = send(client_fd, rx_tx_file->in_name, sizeof(rx_tx_file->in_name),0);
if(send_byte == -1) {
perror("Failed to send file name to client: ");
exit(SEND_TO_CLIENT_ERROR);
}
total_send += send_byte;
}
}
else if (option == SEND_MD5) {
total_send = 0;
send_byte = 0;
while (total_send < strlen(rx_tx_file->md5sum)) {
send_byte = send(client_fd, rx_tx_file->md5sum, sizeof(rx_tx_file->md5sum),0);
if(send_byte == -1){
perror("Failed to send file md5sum to client: ");
exit(-1);
}
total_send += send_byte;
}
}
else if (option == SEND_SIZE) {
send_byte = send(client_fd, &size, sizeof(size),0);
if (send_byte == -1) {
perror("Failed to send size: ");
}
}
}
server:
client_fd = accept(server_fd, (struct sockaddr*) &client_addr, &length)
/*send name of file*/
send_data(client_fd, rx_tx_file, SEND_NAME, 0);
/*send md5 sum*/
take_check_sum(rx_tx_file,rx_tx_file->file_in, 0);
send_data(client_fd, rx_tx_file, SEND_MD5, 0);
/*send size of file*/
size = stats.st_size;
send_data(client_fd, rx_tx_file, SEND_SIZE, size);
remain_data = stats.st_size;
printf("File [%s] ready to send\ncheck sum [%s]\n", rx_tx_file->in_name,rx_tx_file->md5sum);
while (((send_byte = sendfile(client_fd, file_fd, &offset, size)) > 0) && (remain_data > 0))
{
remain_data -= send_byte;
printf("remain %d", remain_data);
}
printf("Succesfully");
Since i work with one client and pass file which should send on server side through command line args, i dont need to wait in while (client_fd = accpet) i just work with one connection and close server. Now its work good. But one question is open, how i should rewrite client side to recv data in a loop. I don't know which size i should recv and because of that i cant write right condition to my while loop. THX all for helping.
TCP is a stream. It has no message boundaries. Your code won't work because of that.
First, you send the name of the file:
send(client_fd, rx_tx_file->in_name, strlen(rx_tx_file->in_name)+1,0)
then you immediately send the md5 sum and then the file size:
send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0)
send(client_fd, &size, sizeof(int),0)
Since the first two strings don't have a fixed number of bytes, it's quite likely that when you try to read the file size or md5 sum from the server you also read the size of the file and maybe even some of the file data.
First, stop trying to put as much of your send and read code as you can into the conditional clause of your if and while statements.
What exactly does
if (send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0) == -1) {
perror("Failed to send file md5sum to client: ");
exit(-1);
}
gain you over
ssize_t bytes_sent = send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0);
if ( bytes_sent < 0 )
{
perror("Failed to send file md5sum to client: ");
exit(-1);
}
Putting all that code into the if clause gains you nothing on the send. And what if strlen(rx_tx_file->md5sum)+1 is 87 and the send() call returns 15? That's a possible return value that your code can't handle because it stuffs everything into the if clause.
ssize_t bytes_sent = send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0);
if ( bytes_sent < 0 )
{
perror("Failed to send file md5sum to client: ");
exit(-1);
}
else if ( bytes_sent < strlen(rx_tx_file->md5sum)+1 )
{
// partial send...
}
That's actually better coded as a loop.
You didn't post your receive code, but if it's in the same style you not only don't gain anything, by putting everything into the if clause you again can't do any decent error detection or correction.
If your file name recv code is similar to
char filename[1024];
if (recv(fd, &filename, sizeof(filename), 0) < 0) {
perror("Failed to read file name: ");
exit(-1);
}
you can't tell what you just received. How many bytes did you just receive? You may have received the file name. You may have received only part of the file name. You may have received the file name, the md5 sum, and some of the file contents itself.
You don't know what you received, and with your code you can't tell. If you zero out the file name and md5 receive buffers and only recv up to one byte less than the size of the buffer, you at least avoid undefined behavior. But if you don't zero out the buffer, or if you read up the the last byte of the buffer, you can also wind up without a nul-terminated string for your filename or md5 sum. And when you then try to treat it as a nul-terminated string you get undefined behavior.
And if you did get extra bytes in the recv calls you make before trying to read the file data, that explains why your code gets stuck - it already read some of the file contents before getting to the loop, so the loop will never see all the content - some of it is gone.
You should avoid using strlen here in your server:
if(send(client_fd, rx_tx_file->in_name, strlen(rx_tx_file->in_name)+1,0) == -1)
Rather just send fixed length string of size sizeof(rx_tx_file->out_name) as you expect in your client
If the filename is smaller just pad it with spaces to make it of length sizeof(rx_tx_file->out_name)
You should also put each receive call in while loop, and add checks that it actually received expected number of bytes, at times recv will just return partial data, you need to post another recv to receive rest of the expected data
I'm doing a little project of TCP connection by C language, and the problem with my code is mentioned in the title. And the following is the uploading part of my code
Client Side:
FILE * fp = fopen(f2d, "rb+");
if(fp == NULL) perror("Fail to upload(client)");
else
{
fseek(fp, 0, SEEK_END);
int filesize = ftell(fp);
memset(buf, '\0', MAX_SIZE);
sprintf(buf, "%d", filesize);
send(serverSocket, buf, strlen(buf), 0); //send the filesize to the server
rewind(fp);
int byteNum = 0, z = 0;
printf("Uploading......\n");
while((z += byteNum) < filesize)
{
memset(buf, '\0', MAX_SIZE);
byteNum = fread(&buf, sizeof(char), sizeof(buf), fp);
printf("Bytes read to buf : %d\n", byteNum);
send(serverSocket, buf, sizeof(buf), 0);
printf("Totally sent bytes: %d\n", z);
}
printf("Upload completed.\n");
}
fclose(fp);
Server Side:
printf("Upload Requested.\n");
f2df = fopen(buf + 4, "wb+");
if(f2df == NULL) perror("Fail to upload(server)");
else
{
memset(buf, 0, MAX_SIZE);
recv(clientSocket, buf, sizeof(buf), 0); //receive the filesize
int filesize = atoi(buf);
int recvNum = 0, recv_Num = 0;
while((recvNum += recv_Num) < filesize)
{
memset(buf, 0, MAX_SIZE);
recv_Num = recv(clientSocket, buf, sizeof(buf), 0);
fwrite(&buf, sizeof(char), sizeof(buf), f2df);
printf("Bytes received from recv: %d\n", recv_Num);
printf("Totally received bytes: %d\n", recvNum);
}
printf("Upload completed.\n");
}
fclose(f2df);
The idea of my code is to send and receive the bytes until it reaches the filesize. But with a very high frequency(well, sometimes it functions normally, all bytes are transferred to the server) that the server seems to miss some bytes sent from the client(and the return value of "recv" function will then be zero after the client has sent all the bytes to the server, which makes the terminating condition of the while loop never be reached), which then causes an infinitive loop on the server side.
P.S. all the other functions are without problems(e.g. sending and receiving filesize, the actual number of the filesize... etc), I had tested them all. And there's also no problem on the client side.
And the following is the screenshot of the problem
So can anyone help me out with that, I've been trying to deal with this for weeks, thanks a lot in advance!
Go through you entire client and server code and fix:
1) All those times where you do not handle correctly the results returned from system calls. This especially applies to the result returned by recv() which can be negative, (error), zero, (peer closed the connection), or some positive number less than, or equal to, the number of bytes requested - the ONLY WAY YOU CAN TELL FOR SURE HOW MANY BYTES HAVE BEEN READ IN TO THE BUFFER. Forget the number of bytes requested in the call and any memset/bzero before, and/or strlen/whatever after, it returns, they are all inadequate/misleading.
2) All those times where you assume that, just because you request the recv() of X bytes and recv() returns a positive number, that number will be X.
3) All those times that you call any str* C-style library calls on any buffers that are not 100% guaranteed to be NULL-terminated.
The main problem is the (not) handling of the send() return value, because even in the absence of any error condition, send() may return a smaller value than requested (e. g. if a buffer is full). After each loop cycle in the client, you increment z by the number of bytes read to buf, but disregard that a smaller number of bytes may have been sent. This leads to the outcome that the client loop completes without having sent all data, despite of incorrectly saying so. Change that e. g. to:
printf("Uploading......\n");
while (z < filesize)
{
byteNum = fread(buf, sizeof(char), sizeof buf, fp);
printf("Bytes read to buf : %d\n", byteNum);
int sent = 0;
while (byteNum > 0)
{
int ret = send(serverSocket, buf+sent, byteNum, 0);
if (ret < 0) { perror("send"); goto fail; }
sent += ret, byteNum -= ret;
printf("Totally sent bytes: %d\n", z += ret);
}
}
printf("Upload completed.\n");
I'm trying to write an experimental client / server program to prove whether the write fails or blocks when the send buffer is full.
Basically, I have an infinite loop on the sender program where I use select() to check if I can write on the buffer (which, I think means that the socket buffer isn't full), if I can write on the buffer than I write() a character. The loop breaks when FD_ISSET(sockfd, &writefds) is false (I can't write on the buffer because it's full).
The reciever program is sleeping for one minute before starting to read(). I expect the sender to fill the buffer within this sleeping time but in fect, the programs never end.
sender:
int main(int argc, char *argv[]) {
char buffer[100];
int sockfd, total = 0, bytes = 0;
fd_set writefds;
sockfd = dial(argv[1], argv[2]);
bzero(buffer, sizeof buffer);
while(1)
{
int ret = 0;
FD_ZERO(&writefds);
FD_SET(sockfd, &writefds);
if((ret = select(sockfd + 1, NULL, &writefds, NULL, 0)) < 0)
{
perror("select");
exit(errno);
}
if(FD_ISSET(sockfd, &writefds))
{
write(sockfd, "a", 1);
total++;
continue;
}
else
{
puts("I can't write in the socket buffer");
break;
}
}
printf("nb chars written: %d\n", total);
return 0;
}
reciever:
int foo(int sockfd) {
char buffer[100];
int t, total = 0;
bzero(buffer, sizeof buffer);
printf("I have a new client\n");
sleep(60);
while((t = read(sockfd, buffer, sizeof buffer)) > 0)
{
total += t;
printf("%d ", total);
}
printf("nb chars read: %d\n", total);
if(t < 0)
{
perror("read");
}
printf("I don't have that client anymore\n");
return 0;
}
Your select timeout is null, so select() will block when the send buffer is full. This means when it returns, the socket is writable, and you'll never get to your code "I can't write in the socket buffer".
See man page http://linux.die.net/man/2/select
If you want a zero timeout, i.e. don't block on select(), you need to pass a pointer to a timeval structure with both fields set to zero.
You're on the right track, but the socket send buffer could be 48k or more. That's a lot of iterations. Try writing 8k at a time, not just one byte. And increase the time before the receiver reads.
NB No real need to test this. It blocks in blocking mode, and fails with EAGAIN/EWOULDBLOCK in non-blocking mode. See the man page.
I am testing my TCP echo server, with Telnet, I can see that the client connects to the server and sends a charcter and in return the server returns a string to the client.
Now my problem is by using this recv() in a infinite loop I can only receive one character (even though the client tends to send a string).
This is how I am doing to receive the datagram from the client
TCP SERVER
while(1)
{
socket = accept(server_socket, (struct sockaddr *)&client_address, (socklen_t)&client_length);
recv(socket, recv_buffer, sizeof(recv_buffer), 0);
printf("Received string from client is %s", recv_buffer);
/*then I send my string to the client*/
send(socket, send_buffer, sizeof(send_buffer), 0);
}
Here is my problem that my recv() routine reads only one character even though the client wants to send a whole string. Is there a way how I can make this recv() routine wait before it receives all the characters from the client and then send a response to the client.
Any suggestions would be appreciated
Regards
Well, you are doing something wrong. Look at the definition of recv:
int recv(int s, void *buf, size_t len, int flags);
So it recieves len amount of bytes. You passed sizeof(recv_buffer) as the len parameter. Now I'm guessing recv_buffer is defined as a char*. Getting the sizeof of a pointer means that you get the amount of bytes necessary to store that pointer, instead of the memory it points to.
Do something like this instead:
const int buf_len = 100;
char recv_buffer[buf_len];
recv(socket, recv_buffer, buf_len, 0);
printf("Received string from client is %s", recv_buffer);
You need to build up the string you are receiving yourself in a loop, using the return value of recv() to find how many bytes you actually got. TCP/IP does not guarantee that all the data sent with one call to send() can be received with one call to recv(). And you must examine the return value of every sockets function you call to check for actual lengths sent/received, and for errors.
Your code is a disaster (sorry for being blunt, but it is best to be straight).
recv() returns the number of bytes actually read. Not only that but it will not clear the previous contents of the buffer and it will fill up right to the the end of the buffer if there is data available. All this means that you cannot treat the content of the buffer as a null terminated string.
You need to do something like:
ssize_t bytesRead = 1;
char recv_buffer[SOME_SIZE];
while (bytesRead > 0)
{
int bytesRead = recv(socket, recv_buffer, SOME_SIZE, 0);
if (bytesRead > 0)
{
// do something with the bytes. Note you cannot guarantee that the buffer contains a valid C string.
}
}
if (bytesRead == -1)
{
// report error from errno
}
else
{
// bytesRead == 0 have reached end of file (i.e. socket closed at other end)
}
There is no way to get recv to wait until the buffer is full before returning. It will wait until there are some bytes available and then return. The same applies to send by the way. You can't assume with one call to send that all of your bytes have actually been sent. You need to put send in a loop too:
ssize_t totalBytesWritten = 0;
ssize_t bytesWritten = 0;
while (bytesWritten >= 0 && totalBytesWritten < bytesToWrite)
{
bytesWritten = send(socket, sendBuffer + totalBytesWritten, bytesToWrite - totalBytesWritten, 0);
if (bytesWritten > 0)
{
totalBytesWritten += bytesWritten;
}
}
if (bytesWritten == -1)
{
// error
}
This code sends and recv s txt file perfectly but cannot do it to otehr formats like .exe or .img. Please help me with these as I need to use htonl or htons??
Take a look!!
Here is the server side recv function ::
if (socket_type != SOCK_DGRAM)
{
fi = fopen (final,"wb");
retval = recv(msgsock, recv_buf, strlen(recv_buf), 0);
/*recv_buf[retval] = '\0';
fprintf (fi,"%s",recv_buf);*/
int i;
i=atoi(recv_buf);
char *q;
q=(char *)malloc(i*sizeof(char));
retval = recv(msgsock, q, strlen(q), 0);
//printf ("%s",q);
fwrite(q,i,1,fi);
fclose(fi);
}
else
{
retval = recvfrom(msgsock,recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&from, &fromlen);
printf("Server: Received datagram from %s\n", inet_ntoa(from.sin_addr));
printf ("SOCK_DGRAM");
}
if (retval == SOCKET_ERROR)
{
fprintf(stderr,"Server: recv() failed: error %d\n", WSAGetLastError());
closesocket(msgsock);
//continue;
}
else
printf("Server: recv() is OK.\n");
if (retval == 0)
{
printf("Server: Client closed connection.\n");
closesocket(msgsock);
//continue;
}
printf("Server: Received %d bytes, data from client\n", retval);
The client side sending function :::
void send_command()
{
int bytesent;
FILE *file_out;
//file_out = fopen(file_path,"rb");
char str_all[100000];//flag [30]="end";
///////////////////////getsize//////////////
char fsize[5];
int filesize;
file_out = fopen(file_path, "rb");
fseek(file_out, 0, SEEK_END);
filesize = ftell(file_out);
rewind (file_out);
itoa (filesize,fsize,10);
/////////////////////////////////////////////
send (ConnectSocket, fsize, strlen (fsize), 0);
char *r = (char *)malloc (filesize * sizeof(char));
fread(r,filesize,1,file_out);
bytesent = send( ConnectSocket, r, strlen(r), 0 );
printf("\nClient: Bytes sent: %ld\n", bytesent);
fclose (file_out);
/*while (fscanf(file_out,"%s",&str_all) != EOF)
{
bytesent = send( ConnectSocket, str_all, strlen(str_all), 0 );
printf("\nClient: Bytes sent: %ld\n", bytesent);
//Sleep(500);
}*/
/*printf("%s",flag);
send( ConnectSocket, flag, strlen(flag), 0 );*/
WSACleanup();
//return 0;
}
OK, there are multiple issues with your program.
You are transferring binary data. The receiver is only going to see a sequence of bytes. There is no way for the receiver to know the end of the data, since all possible values of char are legal data values. If you were sending text data, you could say that a 0 signifies the end of the data, but now you can't. So, you have to decide on a "protocol" between the server and the client—the simplest is that the server sends the length of the data in the first 4 bytes (read up on ntonl() and ntohl() for how to do this portably). Then, the receiver will know exactly how many bytes to read.
You declare the receiver buffer as char *recv_buf, and similarly for recv_buf1. You don't allocate any storage for any of the two pointers, so they aren't pointing to anywhere useful. Then, your recv call is: recv(msgsock, recv_buf, sizeof(recv_buf), 0); This also has problems. The first is the one mentioned above: you don't have storage for recv_buf. The second is that after you do allocate storage for recv_buf, you are taking the size of a char pointer instead of the length of the buffer recv points to. One easy way to solve both the issues would be to declare recv_buf as: char recv_buf[SIZE]; and then use sizeof recv_buf in the recv() call.
I haven't looked at the rest of your code. You probably need a good C and network programming introduction.
I think you're confusing the null-termination of a C string with the end of a packet sent on a socket. There is no "termination" of a packet, it's just a string of bytes. Zeros are completely legal, and you pass (and receive) the length explicitly. You certainly don't need to use the out-of-band facilities to receive multiple packets. Can you be more specific about what you're asking?