[英]transfer files server/client linux C with socket
伙計們,我需要一些幫助。 我必須實現一個服務器/客戶端項目,以便將文件從客戶端傳輸到服務器。 服務器接收 N 個套接字,每個服務器進程一個套接字,使用 fork(),每個套接字(線程)超過 1 個客戶端。 客戶端向服務器發送一個文件,它必須備份它並顯示 TX 速度和字節數。 我解決了許多服務器進程的 TX 問題,但是當我嘗試在同一個套接字上應用並發線程時,它不起作用,沒有收到消息和文件。
服務器使用 1 個參數: number_of_sockets
客戶端使用 3 個參數: server_ip server_port file_name
服務器.c
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/resource.h>
#include<sys/wait.h>
#include<signal.h>
#include<unistd.h>
#include<time.h>
// accepting connections
short int aceptar_conexiones = 1;
//struct for threads
typedef struct{
int fd_socket;
struct sockaddr_in address;
socklen_t sock_length;
}thread_args;
//struct for associate server-process and sockets
typedef struct{
int fd_socket;
pid_t pid_proceso;
}fd_socket_proceso;
//max sockets open in server
unsigned int max_sockets;
//max server-process in server
unsigned int cant_procesos;
//dynamic array for associated struct
fd_socket_proceso *fdsp;
//mutex for sync threads
pthread_mutex_t mutex_thread;
#define TAM_BUFFER 1024
//handler for threads
//void *connection_handler(void *);
void connection_handler(void *);
void finaliza_sockets();
void finaliza_procesos();
void error(const char *);
int main(int argc, char *argv[]) {
int fd_listen_socket, fd_communication_socket, i;
max_sockets = atoi(argv[1]);
struct sockaddr_in listen_address, connection_address;
socklen_t con_addr_len = sizeof(connection_address);
//allocating memory for dynamic array
fdsp = (fd_socket_proceso *)malloc(sizeof(fd_socket_proceso)*max_sockets);
//create and open sockets, grabbing it on array, it starts on 1024 and on, max 5 clients per socket
for(i=0 ; i<max_sockets ; i++){
fd_listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if(fd_listen_socket < 0) error("No se pudo crear el socket.\n");
bzero(&listen_address, sizeof(struct sockaddr_in));
listen_address.sin_family = AF_INET;
listen_address.sin_port = htons(1024+i); // Puede utilizarse cualquier puerto
listen_address.sin_addr.s_addr = htonl(INADDR_ANY); // Cualquier direccion propia
if(bind(fd_listen_socket, (struct sockaddr *)&listen_address, sizeof(struct sockaddr)) < 0) error("No se puede enlazar el socket.\n");
printf("Servidor escuchando en puerto: %d\n",ntohs(listen_address.sin_port));
listen(fd_listen_socket, 5);
fdsp[i].fd_socket = fd_listen_socket;
}
printf("Comenzamos a escuchar conexiones...\n");
fflush(stdout);
//fork per socket
for(i=0 ; i<max_sockets ; i++ , cant_procesos++){
if(!(fdsp[i].pid_proceso=fork())){
while(aceptar_conexiones){
bzero(&connection_address, sizeof(connection_address));
if((fd_communication_socket = accept(fdsp[i].fd_socket, (struct sockaddr *)&connection_address, &con_addr_len))==-1){
finaliza_sockets();
finaliza_procesos();
perror("Error en la comunicacion con el socket");
exit(EXIT_FAILURE);
}
pthread_t thread_cliente;
pthread_attr_t thread_attr;
thread_args t_args;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
t_args.address = connection_address;
t_args.fd_socket = fd_communication_socket;
t_args.sock_length = con_addr_len;
//pthread_create(&thread_cliente, NULL, connection_handler, (void *)&t_args);
connection_handler((void *)&t_args);
}
}
}
finaliza_sockets();
finaliza_procesos();
printf("Server offline.\n");
}
//void *connection_handler(void *t_args)
void connection_handler(void *t_args)
{
thread_args *t = (thread_args *)t_args;
char buffer[TAM_BUFFER];
char *buffer2;
FILE *fp;
char *datos[2];
char file_name[TAM_BUFFER];
char *ip_cliente = inet_ntoa(t->address.sin_addr);
int port_cliente = ntohs(t->address.sin_port);
long int file_size, bytes_restantes;
ssize_t recv_size;
int t_inicio ,t_fin , t_transferencia;
int i;
bzero(buffer, sizeof(buffer));
//1st msg recieve file_size concat using char '°' with file_name
recv_size = recvfrom(t->fd_socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&(t->address), &(t->sock_length));
//parse file_size and file_name
buffer2=strtok(buffer,"°");
for(i=0 ; buffer2 ; i++){
datos[i]=buffer2;
buffer2=strtok(NULL,"°");
}
file_size = atoi(datos[0]);
strcpy(file_name, datos[1]);
//concat file_name for backup
strcat(file_name, ".bkp");
if(fp=fopen(file_name, "r")){
printf("\nError, el fichero %s provisto por el cliente %s ya existe! Este sera omitido por el servidor.\n",file_name, ip_cliente);
fflush(stdout);
close(t->fd_socket);
return;
}else{
if((fp=fopen(file_name,"w"))==NULL){
close(t->fd_socket);
printf("\nError en la creacion del fichero %s provisto por el cliente %s.\n",file_name, ip_cliente);
fflush(stdout);
exit(errno);
}else{
bytes_restantes = file_size;
bzero(buffer, sizeof(buffer));
//calculate begin TX
t_inicio = clock();
//recieve file from sendfile() from client
recv_size = recv(t->fd_socket, buffer, sizeof(buffer), 0);
printf("llego, recv: %ld\n",recv_size);
while(recv_size > 0 && bytes_restantes > 0){
//calculate finish TX
t_fin = clock();
bytes_restantes -= recv_size;
//write on file descriptor
fwrite(buffer, sizeof(char), recv_size, fp);
//calculate TX speed
t_transferencia = t_fin-t_inicio/CLOCKS_PER_SEC;
//print data about TX
printf("Cliente:%s:%d, Fichero:%*s, Bytes recibidos:%*ld, Bytes restantes:%*ld, Velocidad TX:%d (bytes/seg)\r",
ip_cliente, port_cliente, strlen(file_name)*1L, file_name ,sizeof(long int), file_size-bytes_restantes, sizeof(long int),
bytes_restantes, t_transferencia);
fflush(stdout);
bzero(buffer, sizeof(buffer));
//recieve file from sendfile() from client
recv_size = recv(t->fd_socket, buffer, sizeof(buffer), 0);
t_inicio = clock();
}
}
}
printf("\nCliente:%s:%d\tFichero:%s\tTransferencia finalizada.\n",ip_cliente, port_cliente, file_name);
fflush(stdout);
fclose(fp);
close(t->fd_socket);
//pthread_exit(0);
}
//close sockets
void finaliza_sockets(){
int i;
for(i=0 ; i<max_sockets ; i++)
close(fdsp[i].fd_socket);
free(fdsp);
}
//wait for all server-process
void finaliza_procesos(){
int i;
for(i=0 ; i<cant_procesos ; i++)
wait(0);
}
void error(const char *msg){
perror(msg);
exit(errno);
}
客戶端
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<time.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/sendfile.h>
#define TAM_BUFFER 1024
int main(int argc, char *argv[])
{
int fd_socket;
int i;
int tini, tfin, ttransferencia;
struct sockaddr_in server_address;
char *path, *buffer, *vect[30], linea[TAM_BUFFER], linea2[TAM_BUFFER], file_name[TAM_BUFFER/4];
int fd_file;
long int send_bytes, offset, remain_data;
struct stat file_stat;
//file_name , maybe use path
path = argv[3];
if((fd_file = open(path, O_RDONLY))==-1){
perror("error en la apertura del archivo\n");
exit(EXIT_FAILURE);
}
if(fstat(fd_file, &file_stat)<0){
perror("error fstat");
exit(EXIT_FAILURE);
}
//connection with server
fd_socket = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_port = htons(atoi(argv[2]));
server_address.sin_addr.s_addr = inet_addr(argv[1]);
bzero(&(server_address.sin_zero),8);
signal(SIGINT,SIG_DFL);
//establishing connection
if(connect(fd_socket,(struct sockaddr *)&server_address,sizeof(struct sockaddr)) < -1){
perror("error on connection with server");
exit(errno);
}
//parse file_name from path
i=0;
buffer=strtok(path,"/");
while(buffer != NULL)
{
vect[i]=buffer;
buffer=strtok(NULL,"/");
i++;
}
//concat file_size with char '°' and file_name
strcpy(file_name, vect[i-1]);
sprintf(linea,"%ld",file_stat.st_size);
strcat(linea,"°");
strcat(linea, file_name);
//send concatenated char
send_bytes = send(fd_socket,linea,strlen(linea),0);
//send file to server
offset = 0;
remain_data = file_stat.st_size;
while(((send_bytes = sendfile(fd_socket, fd_file,(off_t *) &offset, TAM_BUFFER)) > 0) && (remain_data > 0)){
printf("enviados: %*ld bytes\toffset: %*ld\tbytes restantes: %ld\r",sizeof(send_bytes), send_bytes, sizeof(offset), offset, remain_data);
sizeof(stdout);
remain_data -= send_bytes;
}
close(fd_file);
close(fd_socket);
return 0;
}
正如我所說,我的問題是我需要在每個套接字上進行並發。 我如何管理和同步它們? 我嘗試了基本的東西,但它不起作用,所以我評論了thread_create和thread_exit func。
謝謝你的時間。-
我認為主要結構沒有任何問題。 您正在偵聽多個端口,為每個端口生成一個進程。 每個進程(應該)為每個客戶端生成一個新線程。
您在使用 thread_args.address 的服務器中確實存在競爭條件(如果兩個客戶端快速連續連接,可能會被覆蓋),但很容易避免。 不要在 TCP 套接字上使用 recvfrom —— 這真的沒有意義。 使用 read() 或更好的 fread(),它可以避免 EINTR 問題。 在開始時一次讀取一個字節,直到遇到第一個分隔符(盡管最好更改協議以發送 32 位字而不是 ascii 表示)。
但主要問題是你有一個很大的程序,你只是說“它不起作用”。 只要您希望我們為您調試,發布一些診斷信息如何? 服務器進程是否被創建? 打印什么? 對於第一個客戶端,第二個客戶端還是什么,它是否失敗?
您可以使用 fork() 創建子進程,父進程可以監聽,當客戶端連接時,父進程可以將該客戶端移交給子進程並繼續監聽其他客戶端。 然后當另一個客戶端再次連接 fork() 並且再次連接時,您可以創建另一個子進程來處理該客戶端。 像這樣,您可以同時處理客戶端。
pid = fork()
當 pid = 0 時它是子進程,如果 pid > 0 它是父進程,如果 pid < 0 則分叉錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.