Linux应用程序通过 tcp/udp实现文件传输

基础知识

socket编程——socket_in结构体

sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:
在这里插入图片描述
在这里插入图片描述
参数说明
sin_family主要用于定义是地址族
sin_port主要用来保存端口号
sin_addr主要用来保存IP地址信息
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。

socklen_t类型

socklen_t是一种数据类型,和int差不多,在32位机下,size_t和int的长度相同,都是32 bits,但在64位机下,size_t(32bits)和int(64 bits)的长度是不一样的,socket编程中的accept函数的第三个参数的长度必须和int的长度相同。于是有了socklen_t类型。

名词解释

网络字节顺序 (Network Byte Order) NBO
结构体的sin_port和sin_addr都必须是NBO
本机字节顺序 (Host Byte Order) HBO
一般可视化的数字都是HBO

NBO,HBO二者转换
inet_addr() 将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
inet_aton() 将字符串点数格式地址转化成NBO
inet_ntoa () 将NBO地址转化成字符串点数格式
htons() “Host to Network Short”
htonl() “Host to Network Long”
ntohs() “Network to Host Short”
ntohl() “Network to Host Long”
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型

htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)
htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。

inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。

inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。

TCP/UDP网络通信大概交互图

在这里插入图片描述

UDP用户数据包模式

在这里插入图片描述

报错解决

在测试过程中遇到以下报错:
Connection reset by peer
TCP链接中常见名词是Client Server, 但是网络连接中经常出现Connection reset by peer
在这里插入图片描述
报错分析:
client:客户端
server: 服务端
peer: tcp端。
peer是脱离固定场景的, tcp的任意一端都叫peer.
客户端收到Connection reset by peer代表服务端关闭了链接
服务端收到Connection reset by peer代表客户端关闭了链接

关闭链接的原因可能性就比较多了:
防火墙,路由器等。
解决方法:重启服务端端口


tcp

tcp服务端代码
//tcp-server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
 
#define N 128
 
#define ERRLOG(errmsg) do{\
                perror(errmsg);\
                printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
                exit(-1);\
            }while(0)
 
typedef struct __MSG{
    char buff[N];
    int bytes;
}msg_t;
 
int main(int argc, const char *argv[]){
    if(2 != argc){
        printf("Usage : %s  <port>\n", argv[0]);
        exit(-1);
    }
    //1.创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }
 
    //创建服务器网络信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
  
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    socklen_t addrlen = sizeof(server_addr);
 
    //3.将套接字和网络信息结构体进行绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){
        ERRLOG("bind error");
    }
 
    //4.将服务器的套接字设置成被动监听状态
    if(-1 == listen(sockfd, 5)){
        ERRLOG("listen error");
    }
 
    //定义一个结构体,保存客户端的信息
    struct sockaddr_in client_addr;
    memset(&server_addr, 0, sizeof(client_addr));//清空
    socklen_t clientaddrlen = sizeof(client_addr);
 
    char buff[N] = {0};
    int acceptfd = 0;
ACCEPT:
    //5.阻塞等待客户端连接
    acceptfd = accept(sockfd, (struct sockaddr *)&client_addr, &clientaddrlen);
    if(-1 == acceptfd){
        ERRLOG("accept error");
    }
    printf("客户端 %s:%d 连接到服务器了\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
 
RENAME:
  
    if(-1 == recv(acceptfd, buff, N, 0)){
        ERRLOG("recv error");
    }
    printf("客户端要下载的文件名为:[%s]\n", buff);
 
   
    int fd = open(buff, O_RDONLY);
    if(-1 == fd){
        if(errno == ENOENT){
            printf("文件[%s]不存在\n",buff);
            if(-1 == send(acceptfd, "****NO EXIST****", N, 0)){
                perror("send error");
                goto RENAME;
            }
        }else{
            ERRLOG("open error");
        }
    }
    printf("文件[%s]存在\n",buff);
    if(-1 == send(acceptfd, "****EXIST****", N, 0)){
        ERRLOG("send error");
    }
    int bytes = 0;
  
    msg_t msg;
    memset(&msg, 0, sizeof(msg));
    //循环读取文件内容并发送给客户端
    while((bytes = read(fd, msg.buff, N))>0){
        msg.bytes = bytes;
        if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){
            ERRLOG("send error");
        }
        memset(&msg, 0, sizeof(msg));
    }
    //发送传输完毕的信息
    strcpy(msg.buff, "****OVER****");
    msg.bytes = 0;
    if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){
        ERRLOG("send error");
    }
 
    close(acceptfd);
    
    goto ACCEPT;
 
    close(sockfd);
 
    return 0;
}
tcp客户端代码
//tcp-cliaent
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
 
#define N 128

 
#define ERRLOG(errmsg) do{\
                perror(errmsg);\
                printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
                exit(-1);\
            }while(0)
 
typedef struct __MSG{
    char buff[N];
    int bytes;
}msg_t;
 
int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        exit(-1);
    }
    //1.创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }
 
    //创建服务器网络信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
  
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t addrlen = sizeof(server_addr);
 
    //与服务器建立连接
    if(-1 == connect(sockfd, (struct sockaddr *)&server_addr, addrlen)){
        ERRLOG("connect error");
    }
 
    char filename[N] = {0};
    char buff[N] = {0};
    int fd = 0;
RENAME:
 
    printf("请输入要下载的文件:");
    scanf("%s", filename);
 
    //将要下载的文件发送给服务器
    if(-1 == send(sockfd, filename, N, 0)){
        ERRLOG("send error");
    }
    //接收文件是否存在的信息
    if(-1 == recv(sockfd, buff, N, 0)){
        ERRLOG("recv error");
    }
    
    if(0 == strcmp(buff, "****NO EXIST****")){
        printf("文件不存在\n");
        goto RENAME;
    }else if(0 == strcmp(buff, "****EXIST****")){
        //创建并打开并清空一个文件,准备下载文件内容
        if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){
            ERRLOG("open error");
        }
    }
 
    int bytes = 0;
    msg_t msg;
    memset(&msg, 0, sizeof(msg));
    while(recv(sockfd, &msg, sizeof(msg), 0)>0){
        if(msg.bytes == 0){
            break;
        }
        if(-1 == write(fd, msg.buff, msg.bytes)){
            ERRLOG("write error");
        }
        memset(&msg, 0, sizeof(msg));
    }
    
    close(fd);
    printf("文件下载完成\n");
    //关闭套接字
    close(sockfd);
 
    return 0;
}
测试

在这里插入图片描述

udp

前言:由于udp是不可靠连接,在传输文件的过程中不能保证文件的完整性,该demo只是实现了udp文件传输,不保证准确性
udp服务端
//udp-server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
 
#define N 512
 
#define ERRLOG(errmsg) do{\
                perror(errmsg);\
                printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
                exit(-1);\
            }while(0)
 
typedef struct __MSG{
    char buff[N];
    int bytes;
}msg_t;

int main(int argc, const char *argv[]){
    off_t count=0, m,sz;//long
    long int n;
    char buff[N];
    int acceptfd = 0;
    
    if(2 != argc){
        printf("Usage : %s  <port>\n", argv[0]);
        exit(-1);
    }
    //1.创建用户数据报式套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }
 
    //创建服务器网络信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
  
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    socklen_t addrlen = sizeof(server_addr);
 
    //3.将套接字和网络信息结构体进行绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){
        ERRLOG("bind error");
    }
 
 
    //定义一个结构体,保存客户端的信息
    struct sockaddr_in client_addr;
    memset(&server_addr, 0, sizeof(client_addr));//清空
    socklen_t clientaddrlen = sizeof(client_addr);
 
    
 
 #if 0 
    while(1){
        //接收数据,如果想要给对方回应,就必须保存对方的网络信息结构体
        //如果不回应,后两个参数写 NULL 也行
        if(-1 == recvfrom(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, &clientaddrlen)){
            ERRLOG("recvfrom error");
        }
        printf("%s(%d):%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
        //组装应答信息
        strcat(buff, "--server");
        if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, clientaddrlen)){
            ERRLOG("sendto error");
        }
        memset(buff, 0, N);
    }
    
    
    close(sockfd);
 #endif
 
  
ACCEPT:
    //5.阻塞等待客户端连接
    acceptfd = recvfrom(sockfd,buff,N,0, (struct sockaddr *)&client_addr, &clientaddrlen);
    if(-1 == acceptfd ){
        ERRLOG("recvfrom error");
    }
    printf("客户端 %s:%d 连接到服务器了\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
 
RENAME:

    printf("客户端要下载的文件名为:[%s]\n", buff);
 
    int fd = open(buff, O_RDONLY);
    if(-1 == fd){
        if(errno == ENOENT){
            printf("file [%s] inexistence\n",buff);
            if(-1 == sendto(sockfd, "****NO EXIST****", N, 0,(struct sockaddr *)&client_addr, clientaddrlen)){
                perror("send error");
                goto RENAME;
            }
        }else{
            ERRLOG("open error");
        }
    }
    printf("file [%s] exist\n",buff);
    if(-1 == sendto(sockfd, "****EXIST****", N, 0,(struct sockaddr *)&client_addr, clientaddrlen)){
        ERRLOG("send error");
    }
    int bytes = 0;
  
    msg_t msg;
    memset(&msg, 0, N);
    n=read(fd,buff,N);

    printf("sending.....\n");

	while(n){
		if(n == -1){
            ERRLOG("read fails");
        }
		m=sendto(sockfd,buff,N,0,(struct sockaddr*)&client_addr,clientaddrlen);
		if(m==-1){
			ERRLOG("send error");
		}
		count+=m;
		bzero(buff,N);
        n=read(fd,buff,N);
	}
    msg.bytes = 0;
    printf("send over\n");
    m=sendto(sockfd,buff,0,0,(struct sockaddr*)&client_addr,clientaddrlen);
    printf("The number of bytes transferred : %lld\n",count);

/* 
    //循环读取文件内容并发送给客户端
    while((bytes = read(fd, msg.buff, N))>0){
        msg.bytes = bytes;
        if(-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&client_addr, clientaddrlen)){
            ERRLOG("send error");
        }else{
	}
        memset(&msg, 0, sizeof(msg));
    }
    //发送传输完毕的信息
    strcpy(msg.buff, "****OVER****");
    msg.bytes = 0;
    if(-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&client_addr, clientaddrlen)){
        ERRLOG("send error");
    } */
 
    //close(acceptfd);
    
    //goto ACCEPT;
 
    close(sockfd);
    close(fd);
    return 0;
}

udp客户端代码
//udp-client
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
 
#define N 512
 
#define ERRLOG(errmsg) do{\
                perror(errmsg);\
                printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
                exit(-1);\
            }while(0)
 
typedef struct __MSG{
    char buff[N];
    int bytes;
}msg_t;

int main(int argc, const char *argv[]){
    
    off_t count=0, n; // long type
    char filename[N];
    char buff[N];
    int fd = 0;
    
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        exit(-1);
    }
    
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }
 
   
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t addrlen = sizeof(server_addr);
    
    
#if 0 
    while(1){
        printf("input your msg:");
        fgets(buff, N, stdin);
        buff[strlen(buff)-1] = '\0';//清除 \n
        if(0 == strcmp(buff, "quit")){
            break;
        }
        if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&server_addr, addrlen)){
            ERRLOG("sendto error");
        }
 
        if(-1 == recvfrom(sockfd, buff, N, 0, NULL, NULL)){
            ERRLOG("recvfrom error");
        }
        printf("recv:[%s]\n", buff);
        memset(buff, 0, N);
    }
    //关闭套接字
    close(sockfd);
#endif
   
   

RENAME:
 
    printf("Please enter the file you want to download:");
    scanf("%s", filename);
 
    
    if(-1 == sendto(sockfd, filename, N, 0,(struct sockaddr *)&server_addr, addrlen)){
        ERRLOG("send error");
    }
   
    if(-1 == recvfrom(sockfd, buff, N, 0,(struct sockaddr *)&server_addr, &addrlen)){
        ERRLOG("recv error");
    }
    
    if(0 == strcmp(buff, "****NO EXIST****")){
        printf("file does not exist\n");
        goto RENAME;
    }else if(0 == strcmp(buff, "****EXIST****")){
        if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){
            ERRLOG("open error");
        }
    }
 
   // int bytes = 0;
    //msg_t msg;
    bzero(&buff,N);
    n=recvfrom(sockfd,&buff,N,0,(struct sockaddr *)&server_addr,&addrlen);

    printf("start receiving.......\n");

	while(n){
		if(n==-1){
			ERRLOG("read fails");
		}
		count+=n;
		write(fd,buff,n);
		bzero(buff,N);
        n=recvfrom(sockfd,&buff,N,0,(struct sockaddr *)&server_addr,&addrlen);
        
        
	}

     printf("file download completes\n");
      printf("The number of bytes receive : %lld\n",count);

   /*  memset(&msg, 0, sizeof(msg));
    while(recvfrom(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&server_addr, &addrlen)>0){
        if(msg.bytes == 0){
            break;
        }
        if(-1 == write(fd, msg.buff, msg.bytes)){
            ERRLOG("write error");
        }
        memset(&msg, 0, sizeof(msg));
    } */
    
    close(fd);
    close(sockfd);


    return 0;

}
测试

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值