I am doing a class project right now and it requires me to send data and image using socket. So far I have finished most of it. Data gets sent correctly, small images get sent correctly, but when it comes to large images, only half of it is shown. I checked the characters sent and all of them got sent. So I am not sure where I got wrong. This my function to handle get request. I have tested sending data and it worked, so I think most of this function works but probably just some small details I missed. Please help...thanks!
void handle_get(int sock, const char* url) {
FILE* pFILE;
int file, i;
//file = open(url, O_RDONLY);
pFILE = fopen(++url, "r");
char response_message[1024];
char buffer[1024];
char c;
char *header_type, *file_type;
long file_size;
//char *buffer;
if (pFILE == NULL){
snprintf (response_message, sizeof (response_message), not_found_response_template, url);
write (sock, response_message, strlen (response_message));
error("ERROR opening requested file");
}
file_type = strrchr (url, '.');
//return pointer to the last occurrance of '.'
if (!file_type)
file_type = "html";
else
file_type++;
if (strcasecmp (file_type, "jpg") == 0 || strcasecmp (file_type, "jpeg") == 0)
header_type = "image/jpeg";
else if (strcasecmp (file_type, "gif") == 0)
header_type = "image/gif";
else if (strcasecmp (file_type, "png") == 0)
header_type = "image/png";
else
header_type = "text/html";
snprintf (response_message, sizeof (response_message), ok_response, header_type);
write (sock, response_message, strlen (response_message));
puts(response_message);
int n = 0;
bzero(buffer, 1024);
while (n=fread(buffer, sizeof(char), 1024, pFILE)){
printf("n is %d\n", n);
send (sock, buffer, n, 0);
//write(sock, buffer, n);
bzero(buffer, 1024);
}
fclose (pFILE);
}
Related
I try to transfer a data size of around 100MB over a TCP ipv4 connection socket.
I calculate the CheckSum in the client before sending it to see what the checksum is.
After sending the data file to the server and the server writes it to a new file I calculate again the checksum and I can see a differents.
I think is probably with my send and receive functions.
The Sender function used in CLIENT :
void send_file(FILE *fp, int sockfd) {
int n;
char data[SIZE] = {0};
while (fgets(data, SIZE, fp) != NULL) {
if (send(sockfd, data, sizeof(data), 0) == -1) {
perror("[-]Error in sending file.");
exit(1);
}
bzero(data, SIZE);
}
}
The Writer function the use in the SERVER:
void write_file(int sockfd, char *filename) {
int n;
FILE *fp;
//char *filename = "new_data.txt";
char buffer[SIZE];
fp = fopen(filename, "w");
while (1) {
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0) {
break;
return;
}
fprintf(fp, "%s", buffer);
bzero(buffer, SIZE);
}
}
fgets() and fprintf() are used for reading and writing zero-terminated strings, not arbitrary binary data. fread() and fwrite() are your friends here. Something like:
Client:
#define CHUNK_SIZE 1024
char buffer[CHUNK_SIZE];
while ((num_bytes = fread(buffer, 1, CHUNK_SIZE, fp)) > 0)
{
send(sockfd, buffer, num_bytes, 0);
}
Server:
// Same chunk size and buffer as above
while ((num_bytes = recv(sock, buffer, CHUNK_SIZE, 0)) > 0)
{
fwrite(buffer, 1, num_bytes, fp);
}
Technically fwrite() and send() can write less bytes than you ask them to, and you really should loop until all bytes are written.
You should also technically open your files with modes "rb" and "wb" for binary files.
I am currently trying to work out Downloading / Uploading Files via socket (SOCK_STREAM). The following two functions are what I use to send and receive the data.
I am currently running into the following Issue(s):
The result File sometimes is not the same size as the source File
This Problem is more sever, the larger the file.
I am pretty sure that I am missing something obvious, in my loops maybe or determining the end of the data stream. I spent the past week trying a bunch of different approaches / solutions and this is the closest to a working version i got...
I am thankfull for any advice and review, if i need to provide further information please tell me
Function Sending Data from Server to Client:
void send_file(char *filename, int sock)
{
char data[1024] = {0};
FILE *fp = fopen(filename, "rb");
while (fread(data, sizeof(char), sizeof(data), fp) == 1024) {
if (send(sock, data, sizeof(data), 0) == -1) {
printf("%s%s[-] Error Transmitting File\n\n", KRED, BGHT);
break;
}
bzero(data, sizeof(data));
}
bzero(data, sizeof(data));
strcpy(data, "!EOF!");
send(sock, data, sizeof(data), 0);
bzero(data, sizeof(data));
printf("%s%s[+] Upload Successful\n\n", KGRN, BGHT);
fclose(fp);
}
Function of Client Receiving Data from Server:
void write_file(int sock, char *filepath)
{
FILE *fp;
int n;
char *lastSlash = strrchr(filepath, '\\');
char *filename = lastSlash ? lastSlash +1 : filepath;
char data[1024] = {0};
fp = fopen(filename, "wb");
while (1) {
n = recv(sock, data, sizeof(data), 0);
if (strncmp("!EOF!", data, 5) == 0) {
break;
}
if (n <= 0) {
break;
return;
}
fwrite(data, sizeof(char), sizeof(data), fp);
bzero(data, sizeof(data));
}
fclose(fp);
return;
}
i finally figured it out with the help of the following Post:
Sending files over TCP sockets C++ | Windows
I rewrote the example code to fit my needs. Works like a charm so far.
Thanks to all for giving me some more insight on the topic and helping me figure our the necessary checks on the way!
Here the new Code with a brief explanation:
First thing that needs to be recognized, ist that functions like send() recv() fread() fwrite() are likely to not fill their buffer entirely before passing it on. Meaning if you have a buffer specified with size 100, they might only fill it up to 89 95 or whatever. As a result files are likely to be corrupted. To solve this every call of the send() recv() fread() fwrite() functions needs to be checked.
First you need those two functions both on server and client side. These make sure the entire buffer is being sent / received.
Its basically just looping over send() / recv() until the buffer is filled up.
int RecvBuffer(int sock, char* buffer, int bufferSize, int chunkSize) {
int i = 0;
while (i < bufferSize) {
const int l = recv(sock, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; }
i += l;
}
return i;
}
int SendBuffer(int sock, char* buffer, int bufferSize, int chunkSize) {
int i = 0;
while (i < bufferSize) {
const int l = send(sock, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; }
i += l;
}
return i;
}
Next we need to apply the same check in the functions that are being called to Download / Upload a file. Here I loop over the above functions that fill our Buffer and over the fread() fwrite() until all bytes (fileSize) have been sent. The chunkSize parameter defines the size of each package being sent. I used 65.536 (64kb) so far without any issues.
int RecvFile(int sock, char *filePath, int chunkSize, int fileSize) {
char *lastSlash = strrchr(filePath, '\\');
char *filename = lastSlash ? lastSlash +1 : filePath;
FILE *fp = fopen(filename, "wb");
char buffer[chunkSize];
int i = fileSize;
while (i != 0) {
const int r = RecvBuffer(sock, buffer, (int)__min(i, (int)chunkSize), chunkSize);
if ((r < 0) || !fwrite(buffer, sizeof(char), r, fp)) { break; }
i -= r;
}
bzero(buffer, sizeof(buffer));
fclose(fp);
printf("\n%s%s[+] Download Successful\n\n", KGRN, BGHT);
return -3;
}
int SendFile(int sock, char *fileName, int chunkSize, int fileSize) {
FILE *fp = fopen(fileName, "rb");
char buffer[chunkSize];
int i = fileSize;
while (i != 0) {
const int ssize = __min(i, (int)chunkSize);
if (!fread(buffer, sizeof(char), ssize, fp)) { break; }
const int l = SendBuffer(sock, buffer, (int)ssize, (int)chunkSize);
if (l < 0) { break; }
i -= l;
}
bzero(buffer, sizeof(buffer));
fclose(fp);
printf("\n%s%s[+] Upload Successful\n\n", KGRN, BGHT);
return -3;
}
I'm learning the socket programming and trying to write a simple http server with c. My program can load html/css/javascript files correctly, but the image files can't be loaded. For example, the website icon favicon.ico and <img> of the html file always failed to load. I'm using the code as below to build my simple server:
server.c:
#define CYAN(format, ...) \
printf("\033[1;36m" format "\33[0m\n", ## __VA_ARGS__)
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
socklen_t c_addr_size;
int s_sock;// server socket
int c_sock;// clinet socket
char buf[4096];// user agent
char msg[4096];// file content
char head[1024];// http header
char file[128];// which file requested
void init_server();
void read_request();
void send_file();
int main()
{
init_server();
while (1) {
c_sock = accept(s_sock, NULL, NULL);
if (c_sock != -1) {
int nread = recv(c_sock, buf, sizeof(buf), 0);
read_request();// TODO
CYAN("%d", nread);
CYAN("%s", buf);
send_file();
close(c_sock);
}
}
close(s_sock);
return 0;
}
void init_server()
{
s_sock = socket(AF_INET, SOCK_STREAM, 0);
assert(s_sock != -1);
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
s_addr.sin_port = htons(8000);
int res = bind(s_sock, (struct sockaddr*)&s_addr, sizeof(s_addr));
if (res == -1) { perror("cannot bind"); exit(-1); }
listen(s_sock, 10);// TODO
c_addr_size = sizeof(c_addr);
}
void read_request()
{
int buf_len = strlen(buf);
int i = 0, j = 0;
for (i = 0; i < buf_len - 10; i ++) {
if (buf[i] == 'G' && buf[i + 1] == 'E' && buf[i + 2] == 'T') {// `GET` keyword
i = i + 4;// skip space
while (buf[i] != ' ') {
file[j] = buf[i];
j ++, i ++;
}
file[j] = '\0';
CYAN("%s", file);
return;
}
}
}
void send_file()
{
if (strcmp(file, "/") == 0) {
sprintf(file, "index.html");
is_html = 1;
} else {
sprintf(file, "%s", file + 1);// skip `/`
}
FILE *fp = fopen(file, "r");
// count file length
int file_len = 0;
while (fgets(msg, 1000, fp)) {// read by lines
file_len += strlen(msg);
}
// send http header
sprintf(head,
"HTTP/1.1 200 OK\n"
// "Content-Type: text/html\n"
"Content-Length: %d\n"
"\n", file_len
);
// send file content
CYAN("%d", fseek(fp, 0, SEEK_SET));
memset(msg, 0, sizeof(msg));
send(c_sock, head, strlen(head), 0);
while (fgets(msg, 1000, fp)) {// read by lines
send(c_sock, msg, strlen(msg), 0);
}
fclose(fp);
}
I'm not familiar with http, and I don't know whether I should change the content of the http header when sending images files. How to correct my code, can anyone help me?
FILE *fp = fopen(file, "r");
// count file length
int file_len = 0;
while (fgets(msg, 1000, fp)) {// read by lines
file_len += strlen(msg);
}
No. No. No. No. Never use text processing on binary data.
File length is easy.
FILE *fp = fopen(file, "rb");
fseek(fp, 0, SEEK_END);
long file_len = ftell(fp);
fseek(fp, 0, SEEK_SET);
Now to send it:
send_helper(c_sock, head, strlen(head), 0); /* send header */
while (file_len > 4096) {
int delta = fread(msg, 4096, 1, fp);
if (delta == 0) {
/* handle error */
fclose(fp);
/* need to refactor here; c_sock is useless and needs to be closed */
return;
}
send_helper(c_sock, msg, delta);
file_len -= delta;
}
if (file_len > 0) {
/* last chunk; fread only returns a short count on an actual error so no loop here */
int delta = fread(msg, 4096, 1, fp);
if (delta == 0) {
/* handle error */
fclose(fp);
/* need to refactor here; c_sock is useless and needs to be closed */
return;
}
send_helper(c_sock, msg, delta);
}
fclose(fp);
Unlike fread, send needs a loop to ensure all bytes are sent.
void send_helper(int c_sock, char *msg, size_t size)
{
while (size > 0)
{
ssize_t delta = sendto(c_sock, msg, size, 0);
/*
* not bothering to handle error well--
* we'll just error a few more times and drop out of loop anyway.
* You probably should come back and fix this later though
*/
if (delta <= 0) return;
size -= (size_t)delta;
msg += (size_t)delta;
}
}
when reading the file data into file_buff and then using send() to send the data out to the browser I get weird data like this->(ÿØÿà�JFIF�) instead of the image that is supposed to show up. I can send data like text out to the browser but can't seem to send the image data out.
void *connectionThread(void *socket_desc){
char buffer[100000];
int newsockfd = *(int*)socket_desc;
int n;
magic_t myt = magic_open(MAGIC_ERROR|MAGIC_MIME_TYPE);
magic_load(myt,NULL);
char file_buff [800000];
struct stat filestat;
bzero(buffer,256);
FILE * fp;
while (1)
{memset(buffer, 0, 100000);n = read(newsockfd,buffer,100000);
if (n < 0) error("ERROR reading from socket");
printf("it is something here: %s",buffer);
char *token = strtok(buffer," ");
if(token !=NULL)
token = strtok(NULL, " ");
token = strtok(token,"\n");
token = strtok(token,"/");
fp = fopen ("Koala.jpg", "rb");
write(newsockfd,"Content-Length: 780831\r\n",strlen("Content-Length: 780831\n"));
write(newsockfd,"Content-Type: ",strlen("Content-type: "));
write(newsockfd,magic_file(myt,token),strlen(magic_file(myt,token))); //get Content-type
write(newsockfd,"\n\n",strlen("\n\n"));
//knowing the size of image is 780831
fread(file_buff, sizeof(char), 780831 + 1, fp);
fclose(fp);
send(newsockfd, file_buff, 780831,0);
if ((strncmp(buffer,"quit",4) == 0)) //quit
break;
n = write(newsockfd,"I got your message\n May I have another\n",40);
if (n < 0) error("ERROR writing to socket");
write(newsockfd,"Ok, I am quitting\n",18);
}
close(newsockfd);
magic_close(myt);
pthread_exit(NULL);
return 0;
}
You have to changes the header that you are sending. Set you content-type as Multi-part data.
HTTP/1.1 200 OK\r\ncontent-type: multipart\r\ncontent-length:%ld\r\nConnnection: Close;
I think this should solve your problem.
I have a "file" as a resource. I can only use read(), write() and fstat() it. This file is a text file which I would like to parse.
Normally I use fgets() to read the text file line by line and parse it. How can I do this in this case?
FILE *fp;
char buffer[128];
fp = fopen( "/home/txtfile", "r" );
if (fp == NULL){
perror("file missing");
}
while (fgets (buffer, sizeof(buffer), fp) != NULL) {
//some code
}
How can I do the same with read() ?
Is this right?
int fd = open("/dev/file",O_RDONLY);
if (fd==-1) {
printf("Failed to open file!!!\n");
}
while (fgets (buffer, sizeof (buffer), fd) != NULL) {
//some code
}
Unless your file is huge, if you're using read(), it would be easier to read in the entire file, then operate on the memory buffer, rather than in discrete chunks. That is, unless each line is of a fixed length.
I'd do something like this:
int rc;
int fd = open("data", O_RDONLY); // open the file for reading
if (fd == -1) {
// error
}
// to be thorough, do a stat() here to find how big to make the buffer
struct stat sb;
rc = fstat(fd, &sb);
if (rc == -1) {
// error
}
char *buffer = calloc(1, sb.st_size);
int bytes_read = 0;
// read in entire file; each read() can be incomplete, hence why it's in a loop,
// and reading from/writing to increasing sections of the memory and file
while ((rc = read(fd, (buffer + bytes_read), (sb.st_size - bytes_read))) > 0) {
if (rc == -1) {
// error
}
bytes_read += rc;
}
close(fd);
// Now, to read it line-by-line...
char line[128]; // 128 is arbitrary
int pos = 0;
while ((rc = sscanf(buffer + pos, "%127[^\n]\n", line)) > 0) {
pos += strlen(line) + 1;
// do stuff with line
}
return 0;
Then you can operate on your memory buffer line-by-line by scanning for newlines, or using sscanf(). Also make sure to free() your buffer!
Edit: I've added some example code for using sscanf() to handle your buffer. If you know the format of the lines (you say you're parsing them) you might be able to make better use of sscanf() by using the format specifiers. All of this is untested, by the way.
Something like this :
int fd = open("/dev/file",O_RDONLY);
ssize_t res = 0;
while((res = read(fd, buffer, sizeof(buffer))) > 0) {
//some code
}
if (res < 0) {
//handle error
} else{
//close fd
}
Is this right?
No.
read() is a system call that operates on a Unix file descriptor, not a stdio FILE*. Other than that, it works by reading data from the file and putting it in the buffer you supply.
int fd = open("/dev/file",O_RDONLY);
if (fd==-1)
{
printf("Failed to open file!!!\n");
}
else
{
char buffer[BUF_SIZE];
ssize_t bytesRead = read(fd, buffer, BUF_SIZE);
while (bytesRead > 0)
{
// do something with the buffer
bytesRead = read(fd, buffer, BUF_SIZE);
}
if (bytesRead == -1)
{
// error
}
// bytesRead == 0 => end of file
}