I am a C newbie learning sockets. I wrote a client-server program to receive a file and write it to another file.
The program itself works fine - the file is read by server properly and the client receives it in full, but the client does not exit after receiving all the data.
How would the client know when the entire file is received and how do I make it exit? Following is my client snippet.
Note: I added the condition while (data > 0) as a part of this attempt. Please correct this if wrong.
#define BUFFER 2048
char recived_data[BUFFER];
bzero(recived_data, BUFFER);
FILE *new_file = fopen(“Test.jpg”, “w”);
int data;
do {
data = recv(sockfd, recived_data, BUFFER, 0);
fwrite(recived_data, 1, sizeof(recived_data), new_file);
} while (data > 0);
Your server should close the socket after the whole file content has been sent. This would cause your recv function to return zero and end the client's receive loop.
If you want to keep the connection for some reason, then you would need to send some additional information to the client first (e.g. file length) - so the client knows when one file ends and (potentially) another begins. I'm not sure if you are interested in that, though.
The sender can send the file size before sending the file. The receiver can then receive the file size (say 4 bytes), then call recv() until the full file size has been received.
Related
I want to send files asynchronously. I got on sending a file client->server->another client, but if i want to send a very big file, the client can't send any other commands to server until the file is totally sent. For every file client wants to send, i create a new thread in which i'll read 1kb of the file at a time and sending to the server, then the server will receive the 1kb and send further to the desired client. The problem is that when the client sends the file, the socket is full with bytes from server. I should make one client-server socket for every file i want to send? I've tried everything but nothing was a success.
Creating dedicated sockets for each transfer is one solution, and it's not a bad one unless the number of simultaneous connections is large (only so many IP ports are available on a system, and the server will need twice as many). Threads don't simplify this as much as you might think, and introduce their own challenges; select is a simpler way to efficiently transfer data on multiple sockets from a single thread/process. It works by exposing the underlying operating system's knowledge of which sockets are ready for reading and writing to the program.
The challenge for you with the multi-socket approach, regardless of threading choices, is that the server will have to tell the recipient to open a new connection back to the server for each new transfer. Now you need a command mechansim to tell the recipient to open a new connection for the next file.
Another option would be to open only one socket, but send multiple files simultaneously over the socket. You might accomplish this by sending a data structure containing the next parts of each file instead of simply streaming the file directly. For example, you might send a message that looks something like this (rendered in JSON for clarity, but it would be a valid transport format):
[
{
"name": "file.txt",
"bytes": "some smallish chunk of content",
"eof": false
},
{
"name": "another.txt",
"bytes": "chunk of another.txt content",
"eof": true
}
]
This example is of course naively simplistic, but hopefully it's enough to get the idea across: By structuring the messages you're sending, you can describe to which files, which chunks of bytes belong, and then send multiple chunks of multiple files at once. Because of your client->server->client approach, this seems like the best path forward to me.
using a struct similar to:
struct transferPacket
{
unsigned packetOfFile; // = 0 when starting a new file
unsigned fileNumber; // incremented with each new file
unsigned byteCount;
char payload[ MAX_PAYLOAD_LEN ];
};
When packetOfFile == 0 then starting a new file and payload contains filename
Otherwise indicates which part of file is being transfered.
When byteCount = 0 then EOF for that fileNumber
The above only takes a single TCP socket
multiple files can be transferred at one time.
The receiver knows which file the packet belongs to and which position in the file the payload belongs at.
The sender sends that same number of bytes each time, except for the first packet of a file and the EOF packet or the last data packet of the file
New to Socket Programming. I was making a HTTP server. In the persistent connection, I keep the socket open after the server has sent the first file (1 MB text file) for the 1st HTTP request from the client. After that the server and client programs just sit idle. I have searched a lot and found that recv() keeps looking for data on the socket until the socket connection closes from the server side.
What Should I do in case of a persistent connection?
I am reading a file at the client side (File contains many lines. On each line there's just a file name, that is to be fetched from the server via a persistent connection)
Part of Server code which send the file requested:
while((bytesread = fread(filecontent,sizeof(char), 1024, fp))>0)
{
printf("Bytes Read:%d File Content:%s\n",bytesread,filecontent);
if((n = send(*newsockfd, filecontent, 1024,0))<0)
{
error("SERVER Error: Failed to Write File to Socket");
break;
}
bzero(filecontent,1024);
}
Part of client code where I read the file that has been sent by the server:
for(;;)
{
n=recv(socketfd, buffer, 1024,0);
printf("%d\n",n);
if(n>0)
{ printf("\n in while for reading the file for n bytes %d \n",n);
printf("Response:\n%s",buffer);
}
else if(n<0)
{
disperror("Error reading from socket");
}
if(n==0)
break;
}
Have spent a lot of time. Could not get what to do...
Your client-server protocol is insufficient. The server sends all the bytes of the input file (and more, because you hard-coded 1024 instead of using bytesread). But how should the client understand when the file is complete?
One easy solution is to have the server begin each transmission with the file size. You can simply send a uint64_t (not int because it must be a fixed size on all platforms). That number will represent the total bytes to follow (i.e. the file size). You can get the file size using fseek in the usual way (SEEK_END then ftell or similar).
Then, once your client has received the specified number of bytes, it can break out of the loop.
continues reading even though socket is empty
'Socket is empty' is meaningless. It is either open or closed. In this case it is still open, so your code keeps reading. It will only stop when the peer closes the connection.
If you're going to implement HTTP you need a good working knowledge of RFC 2616, especially the parts relating to content length. Your code doesn't begin to address that currently, and it's too brodpad a topic to cover here. Uffice it to say that keepalive and reading a socket to end of stream are mutually inconsistent.
NB if you get an error reading a socket other than EAGAIN/EWOULDBLOCK you should exit the loop, and you should also print the real error, not some message of your own devising.
NB (2) You don't need to zero the buffer.
NB (3) The correct way to print the buffer is printf("%.*s", n, buffer), but only if n > 0.
i have made one client server application in which client sends file (i.e ODT,PDF,MP3,MP4, etc) and server receives file.
i am dividing file in chunks and then transmits them in while loop.
below i have given main logic for both client and server.
when i do loop-back with 127.0.0.1, this code works successfully.
but when i runs client and server on two different PC, after transmitting a file client exits but server keeps receiving and then i have to press ctrl^C. the size of file at server side reaches over 1GB even if file size at client side is only around 4.2 MB.
and in loopback i am not getting such problem.
please tell me the needed corrections.
client.c
#define SIZE 512 // or anything else
char sendbuff[SIZE];
FILE *fr;
fr = fopen("1.mp3","r");
while(!feof(fr)){
count = fread(sendbuff, SIZE,1,fr);
count = send(clientsd, sendbuff,SIZE,0); //clientsd is socket descriptor.
}
send(clientsd, "xyz", 3, 0); //sending '1'. tells server, transmission is over now.
close(fr);
server.c
#define SIZE 512 // same as client side
char recvbuff[SIZE];
FILE *fw;
fw = fopen("2.mp3","w");
while(1){
count = recv(connsd, recvbuff, SIZE,0);
if(!strcmp(recvbuff,"xyz"))
break;
fwrite(recvbuff,SIZE, 1, fw);
memset(recvbuff,0,SIZE);
}
printf("Exit while\n");
fclose(fw);
any other simple and efficient way to do this ?
NOTE : I have changed my question. here some answers are on my old question where i have transmitted "1" instead of "xyz". which was an error.
The most obvious problem is your stop condition on the server side.
You assume that if the first byte received is '1' (0x31) than the transfer is over, but it might be a byte of the data (if the first byte of the chunk in the file is actually '1'). So you need some other way to signal the end of the file. One possibility is to use a wrapping for each packet sent, for example, before each packet send a specific value (for example '1') followed by the length, and when the transfer is complete send '0' to signal that the transfer is completed.
The other problems I can see are that:
You open the files as read text ("r") and write text ("w") which will stop processing if the EOF sequence appears in the middle of the file, instead you need to open them as read/write binary ("rb" / "wb" respectively).
You use chunks of 512 bytes, what if the file is not a multiple of 512 bytes?
Let me start out by saying that this is a homework assignment for an operating systems class and I am not a programmer, especially not in C. I've been at this for a week now and I am simply stuck and I need help. I have to create TCP client and server applications where linux commands are typed into the client, executed on the server and the output is redirected back to the client. I understand the concept and I have it 90+% working. Commands like "ls", "ls -lpq", "cat somefile", "man somecommand" all work fine. Where I run into trouble is with commands that do not return any information like "mkdir newdir" (if the directory already exists it works fine because I get a response). This is all new to me but it seems to me that my problem is with the servers recv command blocking because there is no information to receive. I don't know how to get past this, I have been working this one issue for a week. I'm running out of time and I also have to implement file upload and download and I don't know where to begin there but I can't even start to work on that until I get past this issue.
Thanks
// this is where I think the problem is
while ((rcvd_bytes = recv(sock_fd, recv_str, sizeof(recv_str), 0)) > 0 ) {
// Zero out the inputCopy buffer
memset(&inputCopy, 0, sizeof(inputCopy)+1);
// Copy the recv_str into a new string so that
// we can work with it.
strcpy(inputCopy, recv_str);
argcount = parsecommand(inputCopy, args);
//Send the message back to client
dup2(sock_fd, 1);
if((status = forkAndExecute(args)) != 0) {
//printf("Command %s returned %d.\n", args[0], status);
}
// as a test is I uncomment the following line mkdir newdir
// returns but the following commands are messed up - as I expect.
//printf("\n");
memset(&recv_str, 0, sizeof(recv_str)+1);
fflush(stdout);
}
if(rcvd_bytes == 0) {
puts("Client disconnected");
fflush(stdout);
}
else if(rcvd_bytes == -1) {
perror("recv failed");
exit(0);
}
Sounds like you need to use select .
if(select(fdmax+1, &my_fdset, 0, 0, NULL) > 0) // there is data available to be read
recv(...)
where fdmax is the largest file descriptor (select wants that +1) and my_fdset is a file descriptor set that you add file descriptors to using FD_SET(sockfd, &my_fdset);.
This will only receive when there is data available. Hope that helps.
EDIT :
I asked a similar question when I was writing a simple client/server program to send/recv strings over a TCP socket. I posted the code that I ended up using on that thread here. Mine is different in that I wanted one program to send and recv but if you look at my sender or receiver functions, you may be able to adapt something to make your program do what you want.
Given your problem description, my suspicion is the client code rather than the server code -- or, actually, the protocol you've implicitly created between the two.
It looks like you're just sending the output of the command directly back to the client by using dup2 to point standard output at the socket. Presumably, the client then reads from the socket to get the output from the command? What does the client do when it sends a command to the server and then gets no reply? A command like mkdir will send nothing back. Is it waiting forever for the server to send command output?
In general, for a synchronous client/server protocol (one where the client sends a command and the server sends a response before the client sends another command), you have to agree on a protocol between the client and the server to clearly indicate when the server is done sending data back and the client should send its next command. You may need to add some way for the server to tell the client that the command completed but sent no output.
I'm so lost. I can't figure out how to stop reading from the server. I'm basically sending a list of every file in the directory from the server to the client. Heres what I'm doing.
SERVER SIDE:
struct dirent *ep = readdir(dp);
while( ep ){
sprintf(buf, "%s", (ep->d_name));
n = write(newsock, buf, MAX );
ep = readdir(dp);
}
CLIENT SIDE:
while( n = read(sock, buf, MAX)){
printf("buf: %s\n" , buf);
}
So the server side stuff works fine. I can see I'm sending all the file names right, but on the client side it reads all the names but just sits waiting to read more.
You either need to send some "EOF" indicator from the server so the client knows to break the loop, or have the server close the socket (after allowing 'some time' for the client to read all the data). This will cause the read to exit with an error condition.
Second is easier on the face of it, first is likely to be more reliable and efficient. Congratulations, you have now built your first communications protocol.
Define a protocol.
A very simple one might be to let the server send an empty (0-length) file name to the client to tell the client that it's done with sending. To successfully do so you might like to delimit each file name by for example an addtionaly \r\n sequence, or similar.