使用线程传输文件

时间:2013-12-02 16:08:43

标签: c multithreading file sockets pthreads

我正在尝试使用线程编写文件传输程序。我想要遵循的格式是:

./server 4501 ..................(will run forever)
./client 4501 add1.txt
./client 4501 bdd1.txt

add1.txt和bdd1.txt将在服务器端保存为add2.txt和bdd2.txt。但是在运行我的代码后,我发现add2.txt包含add1.txt中的字符以及一些额外的字符。

**

更新的解决方案

**

服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>

/* Preprocessor Directives */

#define NTHREADS 50
#define QUEUE_SIZE 5
#define BUFFER_SIZE 256

/* Global counter locked via mutex */

pthread_t threadid[NTHREADS]; // Thread pool
pthread_mutex_t lock;
int counter = 0;

void *threadworker(void *arg)
{

  int sockfd, rw; // File descriptor and 'read/write' to socket indicator
  char *buffer; // Message buffer
  sockfd = (int) arg; // Getting sockfd from void arg passed in

  buffer = malloc(BUFFER_SIZE);
  bzero(buffer, BUFFER_SIZE);

  rw = read(sockfd, buffer, BUFFER_SIZE); // Blocks until there is something to be read in the socket

  FILE *fp; 
  fp=fopen("add2.txt","w");
  fprintf(fp,"%s",buffer);
  //fwrite(buffer, strlen(buffer) + 1, 1, fp);
  //fwrite(buffer,sizeof(char),BUFFER_SIZE, fp);
  fclose(fp);
  printf("%d\n",strlen(buffer));
  printf("the file was received successfully\n");
  printf("the new file created is add2.txt\n");     

  if (rw < 0)
  {
    perror("Error reading form socket, exiting thread");
    pthread_exit(0);
  }

  //printf("New message received: %s", buffer); // String already has newline
  bzero(buffer, BUFFER_SIZE);
  //sprintf(buffer, "Acknowledgement from TID:0x%x", pthread_self());

  /*rw = write(sockfd, buffer, strlen(buffer));

  if (rw < 0)
  {
    perror("Error writing to socket, exiting thread");
    pthread_exit(0);
  }*/

  /* Critical section */

  /*printf("Requesting mutex lock...\n");
  pthread_mutex_lock (&lock);
  printf("Current counter value: %d, upping by 1...\n", counter);
  counter++;
  pthread_mutex_unlock (&lock);
  printf("Done! Mutex unlocked again, new counter value: %d\n", counter);
  */
  close(sockfd);
  //printf("TID:0x%x served request, exiting thread\n", pthread_self());
  pthread_exit(0);

}


int main(int argc, char *argv[])
{

  /* Variable declarations */

  int serv_sockfd, new_sockfd; //Socket identifiers for server and incoming clients
  struct addrinfo flags; // Params used to establish listening socket
  struct addrinfo *host_info; // Resultset for localhost address info, set by getaddrinfo()

  socklen_t addr_size; // Client address size since we use sockaddr_storage struct to store
                       // client info coming in, not using addrinfo as done for host (local) 
                       // by calling getaddrinfo for resolution, which stores results in 
                       // the more convenient addrinfo struct

  struct sockaddr_storage client; // Sockaddr storage struct is larger than sockaddr_in, 
                                  // can be used both for IPv4 and IPv6

  pthread_attr_t attr; // Thread attribute
  int i; // Thread iterator

  /* Start of main program */

  if (argc < 2) {
    fprintf(stderr,"Error: no port provided\n");
    exit(-1);
  }

  memset(&flags, 0, sizeof(flags));
  flags.ai_family = AF_UNSPEC; // Use IPv4 or IPv6, whichever
  flags.ai_socktype = SOCK_STREAM; // TCP
  flags.ai_flags = AI_PASSIVE; // Set address for me 

  if (getaddrinfo(NULL, argv[1], &flags, &host_info) < 0)
  {
    perror("Couldn't read host info for socket start");
    exit(-1);
  }

  serv_sockfd = socket(host_info->ai_family, host_info->ai_socktype, host_info->ai_protocol);

  if (serv_sockfd < 0)
  {
    perror("Error opening socket");
    exit(-1);
  }

  if (bind(serv_sockfd, host_info->ai_addr, host_info->ai_addrlen) < 0)
  {
    perror("Error on binding");
    exit(-1);
  }

  freeaddrinfo(host_info); // Don't need this struct anymore

  pthread_attr_init(&attr); // Creating thread attributes
  pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // FIFO scheduling for threads 
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // Don't want threads (particualrly main)
                                                               // waiting on each other


  listen(serv_sockfd, QUEUE_SIZE); // Pass in socket file descriptor and the size of the backlog queue 
                                   // (how many pending connections can be in queue while another request
                                   // is handled)
  addr_size = sizeof(client);
  i = 0;

  while (1)
  {
    if (i == NTHREADS) // So that we don't access a thread out of bounds of the thread pool
    {
      i = 0;
    }

    new_sockfd = accept(serv_sockfd, (struct sockaddr *) &client, &addr_size);

    if (new_sockfd < 0)
    {
      perror("Error on accept");
      exit(-1);
    }

    pthread_create(&threadid[i++], &attr, &threadworker, (void *) new_sockfd);
    sleep(0); // Giving threads some CPU time
  }

  return 0;
}

客户代码为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>

#define BUFFER_SIZE 256 

int main(int argc, char *argv[])
{
  int sockfd, rw;
  struct addrinfo flags;
  struct addrinfo *server_info;

  char *buffer = malloc(BUFFER_SIZE);

  if (argc < 4)
  {
    fprintf(stderr, "Usage: ./client <hostname/address> <port> <file_path>");
    exit(-1);
  }

  memset(&flags, 0, sizeof(flags)); // Clear so we're not working with garbage
  flags.ai_family = AF_UNSPEC; // IPv4 or IPv6 doesn't matter
  flags.ai_socktype = SOCK_STREAM; // TCP
  flags.ai_flags = AI_PASSIVE; // get the IP for me

  if (getaddrinfo(argv[1], argv[2], &flags, &server_info) < 0) { // Resolve host based on CMD args
    perror("Couldn't find host");
    exit(-1);
  }

  sockfd = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol); // Initialize socket

  if (connect(sockfd, server_info->ai_addr, server_info->ai_addrlen) < 0)
  {
    perror("Couldn't conenct...");
    exit(-1);
  }

  //printf("Connection established, please enter a message:\n");
  bzero(buffer, BUFFER_SIZE);
  //fgets(buffer, BUFFER_SIZE - 1, stdin);

  //char buffer[100];
  FILE *f;
  size_t read=0;
  if((f=fopen(argv[3],"r"))==NULL){
    printf("Failed");
    exit(-1);
  }
    //fseek(f, 0, SEEK_END);
    //len = ftell(f);
  //while (fgets(buffer, strlen(buffer), f) != NULL)    
  //fscanf(f,"%s",buffer);
  //fread(buffer, strlen(buffer)+1, 1, f);
  do{
    read = fread(buffer,sizeof(char),BUFFER_SIZE-1, f);
    if (read > 0) //if return value is > 0
    {
            buffer[BUFFER_SIZE]='\0';
            rw = write(sockfd, buffer, strlen(buffer));
    }
  }
  while(read == BUFFER_SIZE); //end when a read returned fewer items
    fclose(f);


    //write(sock, &len, sizeof(int));
    //write(sock, buffer, len);

  printf("the file was sent successfully");

  //rw = write(sockfd, buffer, strlen(buffer)); // Sending the contents of the buffer - writes using socket file descriptor
  if (rw < 0)
  {
    perror("Failed to send message");
    exit(-1);
  }

  /*bzero(buffer, BUFFER_SIZE);
  rw = read(sockfd, buffer, BUFFER_SIZE); // Read the ENTIRE buffer because we don't know stlen yet 

  if (rw < 0)
  {
    perror("Error reading from socket");
    exit(-1);
  }

  printf("The message is: %s\n", buffer);
  */
  close(sockfd);
  return 0;

}

我的add1.txt文件包含

abcd
efgh
ijkl
mnop

add2.txt文件包含输出:

abcd
efgh
ijkl
mnop
############################################################################################################################################################################################################################################

任何人都可以告诉我当时需要的修改是什么。

提前谢谢你。

1 个答案:

答案 0 :(得分:1)

完成读/写文件后,需要在File * fp(服务器代码)和File * f(客户端代码)上调用fclose。您确实关闭了客户端和服务器之间的连接,但是您没有关闭文件流,这可能是文件为空的原因。因此,在您调用fprintf(服务器代码)和fscanf(客户端代码)之后调用fclose是安全的。