如何保护来自不同子进程的文件读/写?

时间:2017-12-07 09:59:56

标签: c proxy fork mutex

我使用fork()在C中实现了一个代理服务器,用于处理来自客户端的多个请求。我正在尝试为代理实现缓存。为此,我从服务器获取了响应并将其存储到一个文件中,并在下次请求相同的URL时读取该文件。但是,我想当一个子进程将文件写入缓存文件夹时,另一个是已经在缓存中检测到该文件并开始读取它。我尝试过共享的互斥锁,但它们似乎没有用。

/*
     Web Proxy server using C
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <libgen.h>
#include <dirent.h>
#include <stdbool.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/errno.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <openssl/md5.h>


#define BUFFSIZE 10240
#define MAX_PENDING 100
#define RSP_400BR "HTTP/1.1 400 Bad Request\r\nContent-Type:text/html\r\n\r\n"
#define BR_MESSAGE_M "<html><body>400 Bad Request Reason: Invalid Method :<<request method>></body></html>\r\n\r\n"
#define BR_MESSAGE_U "<html><body>400 Bad Request Reason: Invalid URL: <<requested url>></body></html>\r\n\r\n"
#define CACHE_DIR "./cache/"
#define HOST_CACHE_PATH "./cache/hostname_cache.txt"

void calculate_md5sum(char* url, char* md5string);
int send_cached_file(char* url, int client_sock);
int check_file_cache(char* url);
int cache_host_ip(char* hostname, char* ip_string);
int check_host_cache(char *hostname, char* ip_string);

pthread_mutex_t file_guard;

void calculate_md5sum(char* url, char* md5string) 
{
    unsigned char url_md5[16];
    MD5((unsigned char*)url, strlen(url), url_md5);
    for(int i = 0; i < 16; ++i) {
        sprintf(&md5string[i*2], "%02x", (unsigned int)url_md5[i]);
    }
}

int send_cached_file(char* url, int client_sock)
{
    FILE *file_ptr;
    char file_path[50];
    int read_bytes;
    char buffer[BUFFSIZE];
    char md5string[33];
    calculate_md5sum(url, md5string);
    sprintf(file_path,"%s%s", CACHE_DIR, md5string);
    pthread_mutex_lock(&file_guard);
    file_ptr = fopen(file_path, "r");
    while((read_bytes = fread(buffer, 1, sizeof(buffer), file_ptr))) 
    {
        send(client_sock, buffer, read_bytes, 0);
        bzero(buffer, sizeof(buffer));
    }
    fclose(file_ptr);
    pthread_mutex_unlock(&file_guard);
    return 0;
}

int check_file_cache(char* url) 
{
    FILE *file_ptr;
    char md5string[33];
    char file_path[50];
    calculate_md5sum(url, md5string);
    sprintf(file_path,"%s%s", CACHE_DIR, md5string);
    pthread_mutex_lock(&file_guard);
    file_ptr = fopen(file_path, "r");

    if (file_ptr == NULL) 
    {
        pthread_mutex_unlock(&file_guard);
        return 1;
    }
    else
    {
        fclose(file_ptr);
        pthread_mutex_unlock(&file_guard);
        return 0;
    }
}

int cache_host_ip(char* hostname, char* ip_string) 
{
    FILE *host_cache_ptr;
    host_cache_ptr = fopen(HOST_CACHE_PATH, "a+");
    fprintf(host_cache_ptr, "%s,%s\n", hostname, ip_string);
    fclose(host_cache_ptr);
    return 0;
}

// Function to check hostname in cache for IP
int check_host_cache(char *hostname, char* ip_string) 
{
    strcpy(ip_string, "none");
    FILE *host_cache_ptr;
    char *line = NULL;
    size_t length;
    host_cache_ptr = fopen(HOST_CACHE_PATH, "r");
    while((getline(&line, &length, host_cache_ptr))!=-1) {
        if (strstr(line, hostname)) {
            sscanf(line, "%*[^,]%*c%s", ip_string);
            break;
        }
    }
    fclose(host_cache_ptr);
    if (strcmp(ip_string, "none") == 0) {
        return 1;
    }
    else
    {
        return 0;
    }
}

int main(int argc, char *argv[])
{
    FILE *file_ptr;
    char proxy_port[10] = "10001";
    struct sockaddr_in proxy_add;
    struct sockaddr_in client_add;
    struct sockaddr_in server_add;
    struct hostent *hp;
    unsigned int client_add_len;
    int proxy_sock, conn_sock, server_sock;
    int recv_bytes = 0;
    char request_type[10];
    char url[BUFFSIZE];
    char http_version[10];
    char host_name[100];
    char ctop_buffer[BUFFSIZE];
    char stop_buffer[BUFFSIZE];
    char ptoc_buffer[BUFFSIZE];
    int pid;
    char md5string[33];
    char file_path[50];

    if(argc!=2)
    {
        printf("usage: %s [Port]\n",argv[0]);
        exit(1);
    }
    else
    {
        strcpy(proxy_port, argv[1]);
    }

    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&file_guard, &attr);

    /* build address data structure */
    bzero((char *)&proxy_add, sizeof(proxy_add));
    proxy_add.sin_family = AF_INET;
    proxy_add.sin_addr.s_addr = INADDR_ANY;
    proxy_add.sin_port = htons(atoi(proxy_port));

    /* build address data structure for server */
    bzero((char *)&server_add, sizeof(server_add));
    server_add.sin_family = AF_INET;
    server_add.sin_port = htons(atoi("80"));

    /* setup passive open */
    if ((proxy_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    {
        perror("\nproxy_sock creation error");
        exit(1);
    }

    if ((bind(proxy_sock, (struct sockaddr *)&proxy_add, sizeof(proxy_add))) < 0) 
    {
        perror("\nproxy_sock bind error");
        exit(1);
    }   

    listen(proxy_sock, MAX_PENDING);

    while(1)
    {
        client_add_len = sizeof(client_add);
        if ((conn_sock = accept(proxy_sock, (struct sockaddr *)&client_add, &client_add_len)) < 0) 
        {
            perror("\naccept error on proxy");
            continue;
        }

        // Fork for handling multiple connections
        pid = fork();

        if (pid == 0) 
        {
            while(1) 
            {
                char ip_string[20];
                bzero(ctop_buffer, sizeof(ctop_buffer));
                bzero(stop_buffer, sizeof(stop_buffer));
                bzero(ptoc_buffer, sizeof(ptoc_buffer));
                recv_bytes = recv(conn_sock, ctop_buffer, sizeof(ctop_buffer), 0);
                if (recv_bytes == 0)
                {
                    printf("Request Complete\n");
                    close(conn_sock);
                    break;
                }
                sscanf(ctop_buffer, "%s %s %s", request_type, url, http_version);
                // Filter GET requests
                if (strcmp(request_type, "GET") == 0) 
                {
                    if (strstr(url, "https")) 
                    {
                        strcpy(ptoc_buffer, RSP_400BR);
                        send(conn_sock, ptoc_buffer, strlen(ptoc_buffer), 0);
                        bzero(ptoc_buffer, sizeof(ptoc_buffer));
                        strcpy(ptoc_buffer, BR_MESSAGE_U);
                        send(conn_sock, ptoc_buffer, strlen(ptoc_buffer), 0);
                        bzero(ptoc_buffer, sizeof(ptoc_buffer));
                        continue;
                    }

                    if (check_file_cache(url) == 0) 
                    {
                        printf("File found in cache\n");
                        send_cached_file(url, conn_sock);
                    }
                    else
                    {
                        calculate_md5sum(url, md5string);
                        sprintf(file_path, "%s%s", CACHE_DIR, md5string);
                        printf("File NOT found in cache\n");
                        sscanf(url, "%*[^/]%*c%*c%[^/]", host_name);
                        printf("\nHostname:***%s***\n", host_name);

                        if (check_host_cache(host_name, ip_string) == 0) 
                        {
                            server_add.sin_addr.s_addr = inet_addr(ip_string);
                        }
                        else 
                        {
                            // Get IP from host name
                            hp = gethostbyname(host_name);
                            if (hp == NULL) 
                            {
                                perror("\nHost unknown");
                                continue;
                            }
                            // Copy the IP address obtained from gethostbyname() into address structure
                            bcopy(hp->h_addr, (char *)&server_add.sin_addr, hp->h_length);
                            cache_host_ip(host_name, inet_ntoa(server_add.sin_addr));
                        }

                        /*Creating socket for communication to server*/
                        if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
                        {
                            perror("\nserver_sock creation error");
                            exit(1);
                        }

                        // Connect to the server
                        if (connect(server_sock, (struct sockaddr *)&server_add, sizeof(server_add)) < 0 && errno!=EISCONN)
                        {
                            perror("\nunable to connect to host");
                            continue;
                        }

                        // Send the request obtained from client to the server
                        send(server_sock, ctop_buffer, strlen(ctop_buffer), 0);
                        pthread_mutex_lock(&file_guard);
                        file_ptr = fopen(file_path, "a");
                        // Receive response from server and send it to the client
                        while((recv_bytes = recv(server_sock, stop_buffer, sizeof(stop_buffer), 0))) 
                        {
                            printf("Recv bytes: %d\n", recv_bytes);
                            fwrite(stop_buffer, 1, recv_bytes, file_ptr);
                            send(conn_sock, stop_buffer, recv_bytes, 0);
                            bzero(stop_buffer, sizeof(stop_buffer));
                        }
                        fclose(file_ptr);
                        pthread_mutex_unlock(&file_guard);
                    }
                }
                else 
                {
                    bzero(ptoc_buffer, sizeof(ptoc_buffer));
                    strcpy(ptoc_buffer, RSP_400BR);
                    send(conn_sock, ptoc_buffer, strlen(ptoc_buffer), 0);
                    bzero(ptoc_buffer, sizeof(ptoc_buffer));
                    strcpy(ptoc_buffer, BR_MESSAGE_M);
                    send(conn_sock, ptoc_buffer, strlen(ptoc_buffer), 0);
                }

            }
            exit(0);
        }
    }

    return 0;
}

0 个答案:

没有答案