c - C 通过 UDP 套接字传输文件 (linux)
问题描述
我对套接字真的很陌生,大部分代码都是通过从其他线程及其答案中获取代码和建议来编写的,所以这可能很难阅读。
这个线程特别是我从中获取大部分代码的线程。
我的任务包括创建一个能够连接到服务器的应用程序,并向连接到它的其他客户端发送和接收消息和文件。基本上,每台机器同时作为客户端和服务器工作,只要它们连接到服务器,就能够发送和接收数据包。
我已经能够发送和接收消息(字符串),但是,我还不能发送或接收文件,坦率地说,我不知道该怎么做,因为我们之前没有太多关于套接字的解释被赋予了这个任务。
以防万一,套接字必须是 UDP。在这种情况下我不能使用 TCP。
这是代码:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <sys/socket.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h> // para inet_Addr, etc
#include <netdb.h> // estructuras
#include <sys/signal.h>
#include <ctype.h>
#include <openssl/md5.h>
#include "PB.h"
#include <fcntl.h>
#include <ctime>
#include <iomanip>
#define MAX_LARGO_IP 16
#define MAX_USPW 25
#define MAX_LARGO_MENSAJE 255
#define MAX_NOMBRE_ARCHIVO 20
#define MAX_LARGO_ARCHIVO 65535
#define BUFLEN 503
int fd1;
using namespace std;
void printProgress (double percentage);
unsigned long fsize(char* file)
{
FILE * f = fopen(file, "r");
fseek(f, 0, SEEK_END);
unsigned long len = (unsigned long)ftell(f);
fclose(f);
return len;
}
int main(int argc, char * argv[]){
if (argc < 4)
{
cout << "\33[46m\33[31m[ERROR]:" << " Faltan argumentos: port, ipAuth, portAuth.\33[00m\n";
exit (1);
}
// Signal handler
// *********************************
struct sigaction sa;
memset (&sa, 0, sizeof (sa));
sa.sa_handler = &manejadorSenhales;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGKILL, &sa, NULL);
signal(SIGALRM, SIG_IGN);
// **********************************
cout << "\33[34mRedes de Computadoras 2018\33[39m: Sistema de Mensajeria.\nEscuchando en el puerto " << argv[1] << ".\nProceso de pid: " << getpid() << ".\n";
char * user = new(char[MAX_USPW]);
char * pass = new(char[MAX_LARGO_MENSAJE]);
char * buf = new(char[MAX_LARGO_MENSAJE]);
char * msjAnterior = new(char[MAX_LARGO_MENSAJE]);
char * mensaje = new(char[MAX_LARGO_MENSAJE]);
cout << "Usuario: ";
cin >> user;
cout << "Clave: ";
cin >> pass;
strcpy(mensaje, user);
strcat(mensaje, "-");
///////////////////
// AUTENTICATION // THIS PART CONNECTS YOU TO THE SERVER //////////////////////////////
///////////////////
int numbytes;
unsigned long ultimoRemitente = 0;
strcpy(msjAnterior, "\0"); //inicializo el buffer como vacio
fd1 = socket(AF_INET, SOCK_STREAM, 0);
if (fd1 < 0){
cout << "ERROR: socket()\n";
exit (0);
}
struct hostent *he;
struct sockaddr_in server;
socklen_t longitudServer = sizeof(server);
if ((he=gethostbyname(argv[2]))==NULL){
printf("ERROR: gethostbyname()\n");
exit(0);
}
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[3]));
server.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(server.sin_zero),8);
if(connect(fd1, (struct sockaddr *)&server, sizeof(struct sockaddr))==-1){
printf("ERROR: connect()\n");
exit(0);
}
if ((numbytes=recv(fd1,buf,MAX_LARGO_MENSAJE,0)) == -1){
printf("ERROR: recv()\n");
exit(0);
}
buf[numbytes-2]='\0';
if (strcmp(buf, "Redes 2019 - Obligatorio 2 - Autenticacion de Usuarios") != 0){
printf("ERROR: protocolo incorrecto()\n");
exit(0);
}
mensaje[strlen(mensaje) + 1] = '\n';
mensaje[strlen(mensaje)] = '\r';
send(fd1, mensaje, strlen(mensaje),0);
if ((numbytes=recv(fd1,buf,MAX_LARGO_MENSAJE,0)) == -1){
printf("ERROR: recv()\n");
exit(0);
}
buf[numbytes-2]='\0';
if (strcmp(buf, "NO") == 0){
printf("ERROR: Autenticacion Incorrecta.\n");
exit(0);
}
if ((numbytes=recv(fd1,buf,MAX_LARGO_MENSAJE,0)) == -1){
printf("ERROR: recv()\n");
exit(0);
}
buf[numbytes-2]='\0';
struct sockaddr_in cliente;
socklen_t largoCliente = sizeof(cliente);
// ALL OF THIS IS USED TO SHOW DATE AND TIME OF EACH MESSAGE
time_t timer, timerNew;
tm fechaActual, fechaNew;
time(&timer);
fechaActual=*(localtime(&timer));
int archivo, n;
cout << "Bienvenido " << buf << endl;
close (fd1);
int pid = fork();
if (pid < 0){
cout << "\33[46m\33[31m[ERROR]:" << " Imposible bifurcar.\33[00m\n";
exit(0);
}
if (pid > 0){
/////////////////
// SERVER SIDE // WHERE MESSAGES AND FILES ARE RECEIVED ///////////////
/////////////////
printf("\33[34mRx\33[39m: Iniciada parte que recepciona mensajes. Pid %d\n", getpid());
char ipOrigen[MAX_LARGO_IP];
unsigned int fd_rx;
char fname[MAX_NOMBRE_ARCHIVO];
char flen[20];
char messagef[BUFLEN];
FILE *fp;
fd_rx = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd_rx < 0){
cout << "ERROR: socket()\n";
exit (0);
}
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[1]));
server.sin_addr.s_addr = INADDR_ANY;
bzero(&(server.sin_zero),8);
if(bind(fd_rx,(struct sockaddr*)&server, sizeof(struct sockaddr))){
cout << "ERROR: bind()\n";
exit (0);
}
while (true)
{
//sleep(3);
if(recvfrom(fd_rx, buf, MAX_LARGO_MENSAJE, 0, (struct sockaddr *)&cliente, &largoCliente) == -1) // Si da error, poner sizeof(server) o &longitudServer
printf("ERROR: 1 recvfrom()\n");
else
{
time(&timerNew);
fechaNew=*(localtime(&timerNew));
// MAKES SURE A MESSAGE ISN'T SENT TWICE IN A SHORT AMOUNT OF TIME
if((strcmp(msjAnterior,buf) != 0) || (fechaNew.tm_sec > fechaActual.tm_sec ) || (ultimoRemitente != cliente.sin_addr.s_addr))
{
time(&timer);
fechaActual=*(localtime(&timer));
cout << setfill('0');
cout << "[" << fechaActual.tm_year + 1900 << "." << setw(2) << fechaActual.tm_mon + 1 << "." << setw(2) << fechaActual.tm_mday
<< " " << setw(2) << fechaActual.tm_hour << ":" << setw(2) << fechaActual.tm_min << "] " ; // SHOWS DATE AND TIME
strcpy(ipOrigen, inet_ntoa(cliente.sin_addr));
if(strcmp(buf, "peer_discover")==0)
cout << "envio de peer discover" << endl;
else if (strcmp(buf, "enviar") == 0)
{
///////////////////////////////////////////////////////////////
////// THIS IS WHERE THE FILE TRANSFER PART STARTS ////////////
///////////////////////////////////////////////////////////////
if(recvfrom(fd_rx, messagef, 20, 0, (struct sockaddr *)&cliente, &largoCliente) == -1)
printf("ERROR: 2 recvfrom()\n");
memset(messagef, 0, 503);
if(recvfrom(fd_rx, messagef, 20, 0, (struct sockaddr *)&cliente, &largoCliente) == -1)
printf("ERROR: 3 recvfrom()\n");
strcpy(flen, messagef);
unsigned long mm = atoi(messagef);
fp = fopen(flen, "wb");
int itr=1;
memset(messagef, 0, 503);
while(itr*503 < mm)
{
if(recvfrom(fd_rx, messagef, 503, 0, (struct sockaddr *)&cliente, &largoCliente) == -1){
printf("ERROR: 4 recvfrom()\n");
exit(0);
}
fwrite(messagef, 503, 1, fp);
memset(messagef, 0, 503);
itr++;
}
if (recvfrom(fd_rx, messagef, (mm % 503), 0, (struct sockaddr *)&cliente, &largoCliente) == -1)
printf("ERROR: 5 recvfrom()\n");
fwrite(messagef, (mm % 503), 1, fp);
memset(messagef, 0, 503);
fclose(fp);
}
///////////////////////////////////////////
////// FILE TRANSFER PART ENDS ////////////
///////////////////////////////////////////
else
cout << ipOrigen << " " << buf << endl;
strcpy(msjAnterior, buf);
ultimoRemitente = cliente.sin_addr.s_addr;
strcpy(buf, "\0");
}
}
}
}
else if (pid == 0)
{
////////////////////
// CLIENT SIDE ///// WHERE MESSAGES AND FILES ARE SENT FROM ////////////////
////////////////////
printf("\33[34mTx\33[39m: Iniciada parte que transmite mensajes. Pid %d\n", getpid());
char ipDestino[MAX_LARGO_IP];
char buffS[MAX_LARGO_MENSAJE + 1];
char message[MAX_LARGO_MENSAJE];
char * enviar = new(char[7]);
strcpy(enviar, "enviar");
char fname[BUFLEN];
unsigned long siz;
char str[10];
FILE *f;
int enviados = 0;
unsigned int fd_tx;
fd_tx = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd_tx < 0){
cout << "ERROR: socket()\n";
exit (0);
}
while (true)
{
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[1]));
bzero(&(server.sin_zero),8);
cin >> ipDestino;
if (ipDestino[0] == '*'){
int broadcastPermission = 1;
if (setsockopt(fd_tx, SOL_SOCKET, SO_BROADCAST, (void *) &broadcastPermission,sizeof(broadcastPermission)) < 0){
fprintf(stderr, "setsockopt error");
exit(1);
}
}
else if(ipDestino[0] == '&')
cout << "Se hace peer_discover" << endl;
else if ((he=gethostbyname(ipDestino))==NULL){
printf("ERROR: gethostbyname()\n");
exit(0);
}
server.sin_addr = *((struct in_addr *)he->h_addr);
cin.ignore(1,' ');
cin.getline(buffS,MAX_LARGO_MENSAJE,'\n');
printf("ipDestino: %s, mensaje: %s \n", ipDestino, buffS);
if (buffS[0] == '&')
{
///////////////////////////////////////////////////////////////
////// THIS IS WHERE THE FILE TRANSFER PART STARTS ////////////
///////////////////////////////////////////////////////////////
cout << "\nSE QUIERE MANDAR ARCHIVO\n";
sendto(fd_tx, enviar, sizeof(char), 0, (struct sockaddr *)& server, sizeof(server));
cout << "\nSE MANDO NOMBRE ARCHIVO\n";
int i = 8;
int j = 0;
while (buffS[i] != '\n'){
fname[j++] = buffS[i++];
}
cout << "nombre archivo: " << fname << endl; // muestra el nombre para saber si esta bien. ELIMINAR
sleep(1);
sendto(fd_tx, fname, 20, 0, (struct sockaddr *)&server, sizeof(server));
siz = fsize(fname);
sprintf(str, "%d", siz);
sendto(fd_tx, str, 20, 0, (struct sockaddr *)&server, sizeof(server));
f = fopen(fname, "rb");
memset(message, 0, 503);
fread(message, 503, 1, f);
int itr = 1;
while(itr*503 < siz)
{
if(sendto(fd_tx, message, 503, 0, (struct sockaddr*)&server, sizeof(server)) == -1){
cout << "ERROR: 1 send()\n";
exit(0);
}
memset(message, 0, 503);
fread(message, 503, 1, f);
itr++;
}
fread(message, (siz % 503), 1, f);
if(sendto(fd_tx, message, (siz % 503), 0, (struct sockaddr*)&server, sizeof(server)) == -1){
cout << "ERROR: 2 send()\n";
exit(0);
}
fclose(f);
}
///////////////////////////////////////////
////// FILE TRANSFER PART ENDS ////////////
///////////////////////////////////////////
else{
// SEND MESSAGES
enviados = sendto (fd_tx, buffS, sizeof(char) * (strlen(buffS) + 1), 0, (struct sockaddr *)&server, sizeof(server));
if(enviados != -1)
cout << enviados << "bytes enviados.\n" ;
else
cout << "No se pudo enviar el mensaje.\n";
}
}
}
}
对不起。服务器端和客户端必须在同一个文件中,这真的很长。
在尝试发送文件时,程序在 snedto 之后的第二个 cout 中返回 SIGSEGV“段违规”。
cout << "\nSE QUIERE MANDAR ARCHIVO\n";
sendto(fd_tx, enviar, sizeof(char), 0, (struct sockaddr *)& server, sizeof(server));
cout << "\nSE MANDO NOMBRE ARCHIVO\n"; <--- RIGHT HERE
我猜服务器端有问题,但我不知道它到底是什么。
任何帮助将不胜感激。
解决方案
推荐阅读
- node.js - 在一个应用程序中提取所有社交应用程序的数据
- excel - 带有 vbNarrow 的 VBA StrConv 抛出运行时错误“5”
- tensorflow - how to train image segmentation task on tpu's in colab?
- javascript - 使用 DOMPurify 清理 HTML 时允许属性的安全含义
- docker - is a running container created by each RUN command during multistage build?
- java - 为什么hazelcast不能用imap和hazelcastjsonvalue查询不正确的结果
- prolog - Prolog规则可以条件是在日期之前,但不能在日期之后,为什么?
- vb.net - 将找到的颜色四舍五入到指定的调色板颜色
- sql-server - 在 SQL Server 中从联合结果中选择顶部
- c - 如何在 C 中从 NTP 服务器设置 UTC 时间