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

news/2024/5/18 14:00:57 标签: tcp/ip, udp, linux

基础知识

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_289">udp

udpdemoudp_290">前言:由于udp是不可靠连接,在传输文件的过程中不能保证文件的完整性,该demo只是实现了udp文件传输,不保证准确性

udp_291">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_459">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;

}

测试

在这里插入图片描述


http://www.niftyadmin.cn/n/170727.html

相关文章

[oeasy]python0 113_字符编码_VT100控制码_iso_8859_1_拉丁字符_latin

拉丁字符 回忆上次内容 上次回顾了字型编码的进化过程 7-bit 的 点阵字库终于让 字母、数字、标点 明确了字型 但是 7-bit 的 ascii中 没有法文字符的位置 如果扩展位为1 不同的计算机厂商 有各自不同的 扩展方式 这噩梦 比法语不兼容 更可怕&#xff01;&#x1f631;这以…

0X30数学知识 - 质数

定义&#xff1a; 若一个正整数无法被除了1和它自身之外的任何自然数整除&#xff0c;则称该数为质数(或素数)&#xff0c;否则称该正整数是合数。 浅谈&#xff1a; 在整个自然数集合中&#xff0c;质数的数量不多&#xff0c;分布比较稀疏&#xff0c;对于一个足够大的整数N…

C++ 机房预约系统(三):登录模块——全局文件添加、全局函数登录函数封装、学生、老师、管理员的登陆具体实现

6、 登录模块 6.1 全局文件添加 功能描述&#xff1a; 不同的身份可能会用到不同的文件操作&#xff0c;我们可以将所有的文件名定义到一个全局的文件中在头文件中添加 globalFile.h 文件&#xff0c;把常用文件的文件名写成纯大写的宏常量在同级目录下创建以下txt文件 代码…

SpringCloud笔记(Hoxton)——Bus消息总线

简介 ESB&#xff08;企业服务总线&#xff09;是OA &#xff08;面向服务架构&#xff09;的一种常见的设计实践 基于总线的设计&#xff0c;借鉴了计算机内部硬件组成的设计思想&#xff1a;通过总线传输数据。在分布式系统中&#xff0c;不同子系统之间需要实现相互通信和远…

C++基础算法③——排序算法(选择、冒泡附完整代码)

排序算法 1、选择排序 2、冒泡排序 1、选择排序 基本思想&#xff1a;从头至尾扫描序列&#xff0c;每一趟从待排序元素中找出最小(最大)的一个元素值&#xff0c;然后与第一个元素交换值&#xff0c;接着从剩下的元素中继续这种选择和交换方式&#xff0c;最终得到一个有序…

无线鼠标怎么连接电脑?学会这招不求人!

案例&#xff1a;无线鼠标怎么连接电脑 “之前一直用的是有线鼠标&#xff0c;最近为了方便&#xff0c;买了个无线鼠标&#xff0c;但是却不知道如何连接电脑&#xff0c;想问下朋友们有什么比较好的建议吗&#xff1f;谢谢&#xff01;” 无线鼠标的使用方便了我们的日常工…

软考-信息系统项目管理师 - 第9章 项目人力资源管理

9.1 项目人力资源管理概念 9.1.1 项目团队 由为完成项目而承担不同角色与职责的人员组成。 9.1.2 项目管理团队 是项目团队的一部分&#xff0c;负责项目管理和领导活动&#xff0c;如各项目阶段的启动、规划、执行、监督、控制和收尾 9.1.3 领导和管理 工作&#xff1a;…

PO、VO、DAO、BO、DTO、POJO 能分清吗?

《阿里巴巴Java开发规范》关于领域模型的部分介绍如下 分层领域模型规约: DO(Data Object):此对象与数据库表结构一一对应&#xff0c;通过 DAO 层向上传输数据源对象。DTO(Data Transfer Object):数据传输对象&#xff0c;Service 或 Manager 向外传输的对象。BO(Business Ob…