Linux网络编程之基于UDP实现可靠的文件传输示例

了解网络传输协议的人都知道,采用TCP实现文件传输很简单。相对于TCP,由于UDP是面向无连接、不可靠的传输协议,所以我们需要考虑丢包和后发先至(包的顺序)的问题,所以我们想要实现UDP传输文件,则需要解决这两个问题。方法就是给数据包编号,按照包的顺序接收并存储,接收端接收到数据包后发送确认信息给发送端,发送端接收确认数据以后再继续发送下一个包,如果接收端收到的数据包的编号不是期望的编号,则要求发送端重新发送。

下面展示的是基于linux下C语言实现的一个示例程序,该程序定义一个包的结构体,其中包含数据和包头,包头里包含有包的编号和数据大小,经过测试后,该程序可以成功传输一个视频文件。

具体实现代码如下:

server端代码如下:

/*************************************************************************
  > File Name: server.c
  > Author: SongLee
 ************************************************************************/
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<netdb.h>
#include<stdarg.h>
#include<string.h> 

#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512 

/* 包头 */
typedef struct
{
  int id;
  int buf_size;
}PackInfo; 

/* 接收包 */
struct SendPack
{
  PackInfo head;
  char buf[BUFFER_SIZE];
} data; 

int main()
{
  /* 发送id */
  int send_id = 0; 

  /* 接收id */
  int receive_id = 0; 

  /* 创建UDP套接口 */
  struct sockaddr_in server_addr;
  bzero(&server_addr, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(SERVER_PORT); 

  /* 创建socket */
  int server_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if(server_socket_fd == -1)
  {
    perror("Create Socket Failed:");
    exit(1);
  } 

  /* 绑定套接口 */
  if(-1 == (bind(server_socket_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))))
  {
    perror("Server Bind Failed:");
    exit(1);
  } 

  /* 数据传输 */
  while(1)
  {
    /* 定义一个地址,用于捕获客户端地址 */
    struct sockaddr_in client_addr;
    socklen_t client_addr_length = sizeof(client_addr); 

    /* 接收数据 */
    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);
    if(recvfrom(server_socket_fd, buffer, BUFFER_SIZE,0,(struct sockaddr*)&client_addr, &client_addr_length) == -1)
    {
      perror("Receive Data Failed:");
      exit(1);
    } 

    /* 从buffer中拷贝出file_name */
    char file_name[FILE_NAME_MAX_SIZE+1];
    bzero(file_name,FILE_NAME_MAX_SIZE+1);
    strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
    printf("%s\n", file_name); 

    /* 打开文件 */
    FILE *fp = fopen(file_name, "r");
    if(NULL == fp)
    {
      printf("File:%s Not Found.\n", file_name);
    }
    else
    {
      int len = 0;
      /* 每读取一段数据,便将其发给客户端 */
      while(1)
      {
        PackInfo pack_info; 

        if(receive_id == send_id)
        {
          ++send_id;
          if((len = fread(data.buf, sizeof(char), BUFFER_SIZE, fp)) > 0)
          {
            data.head.id = send_id; /* 发送id放进包头,用于标记顺序 */
            data.head.buf_size = len; /* 记录数据长度 */
            if(sendto(server_socket_fd, (char*)&data, sizeof(data), 0, (struct sockaddr*)&client_addr, client_addr_length) < 0)
            {
              perror("Send File Failed:");
              break;
            }
            /* 接收确认消息 */
            recvfrom(server_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&client_addr, &client_addr_length);
            receive_id = pack_info.id;
          }
          else
          {
            break;
          }
        }
        else
        {
          /* 如果接收的id和发送的id不相同,重新发送 */
          if(sendto(server_socket_fd, (char*)&data, sizeof(data), 0, (struct sockaddr*)&client_addr, client_addr_length) < 0)
          {
            perror("Send File Failed:");
            break;
          }
          /* 接收确认消息 */
          recvfrom(server_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&client_addr, &client_addr_length);
          receive_id = pack_info.id;
        }
      }
      /* 关闭文件 */
      fclose(fp);
      printf("File:%s Transfer Successful!\n", file_name);
    }
  }
  close(server_socket_fd);
  return 0;
}

client端代码如下:

/*************************************************************************
  > File Name: client.c
  > Author: SongLee
 ************************************************************************/
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<netdb.h>
#include<stdarg.h>
#include<string.h> 

#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512 

/* 包头 */
typedef struct
{
  int id;
  int buf_size;
}PackInfo; 

/* 接收包 */
struct RecvPack
{
  PackInfo head;
  char buf[BUFFER_SIZE];
} data; 

int main()
{
  int id = 1; 

  /* 服务端地址 */
  struct sockaddr_in server_addr;
  bzero(&server_addr, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  server_addr.sin_port = htons(SERVER_PORT);
  socklen_t server_addr_length = sizeof(server_addr); 

  /* 创建socket */
  int client_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if(client_socket_fd < 0)
  {
    perror("Create Socket Failed:");
    exit(1);
  } 

  /* 输入文件名到缓冲区 */
  char file_name[FILE_NAME_MAX_SIZE+1];
  bzero(file_name, FILE_NAME_MAX_SIZE+1);
  printf("Please Input File Name On Server: ");
  scanf("%s", file_name); 

  char buffer[BUFFER_SIZE];
  bzero(buffer, BUFFER_SIZE);
  strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name)); 

  /* 发送文件名 */
  if(sendto(client_socket_fd, buffer, BUFFER_SIZE,0,(struct sockaddr*)&server_addr,server_addr_length) < 0)
  {
    perror("Send File Name Failed:");
    exit(1);
  } 

  /* 打开文件,准备写入 */
  FILE *fp = fopen(file_name, "w");
  if(NULL == fp)
  {
    printf("File:\t%s Can Not Open To Write\n", file_name);
    exit(1);
  } 

  /* 从服务器接收数据,并写入文件 */
  int len = 0;
  while(1)
  {
    PackInfo pack_info; 

    if((len = recvfrom(client_socket_fd, (char*)&data, sizeof(data), 0, (struct sockaddr*)&server_addr,&server_addr_length)) > 0)
    {
      if(data.head.id == id)
      {
        pack_info.id = data.head.id;
        pack_info.buf_size = data.head.buf_size;
        ++id;
        /* 发送数据包确认信息 */
        if(sendto(client_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&server_addr, server_addr_length) < 0)
        {
          printf("Send confirm information failed!");
        }
        /* 写入文件 */
        if(fwrite(data.buf, sizeof(char), data.head.buf_size, fp) < data.head.buf_size)
        {
          printf("File:\t%s Write Failed\n", file_name);
          break;
        }
      }
      else if(data.head.id < id) /* 如果是重发的包 */
      {
        pack_info.id = data.head.id;
        pack_info.buf_size = data.head.buf_size;
        /* 重发数据包确认信息 */
        if(sendto(client_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&server_addr, server_addr_length) < 0)
        {
          printf("Send confirm information failed!");
        }
      }
      else
      { 

      }
    }
    else
    {
      break;
    }
  } 

  printf("Receive File:\t%s From Server IP Successful!\n", file_name);
  fclose(fp);
  close(client_socket_fd);
  return 0;
}

感兴趣的朋友可以动手测试一下该程序,相信会对大家的Linux下C语言网络编程带来一定的帮助。

(0)

相关推荐

  • linux系统之间通过nfs网络文件系统挂载设置方法

    NFS简介 NFS是Network File System的简写,即网络文件系统. 网络文件系统是FreeBSD支持的文件系统中的一种,也被称为NFS. NFS允许一个系统在网络上与他人共享目录和文件.通过使用NFS,用户和程序可以像访问本地文件一样访问远端系统上的文件. NFS好处 以下是NFS最显而易见的好处: 1.本地工作站使用更少的磁盘空间,因为通常的数据可以存放在一台机器上而且可以通过网络访问到. 2.用户不必在每个网络上机器里头都有一个home目录.Home目录 可以被放在NFS服务

  • Linux网络相关配置文件

    Linux网络相关配置文件 一 网络参数与配置文件对应关系 所需要的网络参数 主要配置文件命名 重要参数 IP Netmask DHCP Gateway等 /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=网卡名称 BOOTPROTO=是否使用dhcp HWADDR = 是否加入网卡MAC地址 IPADDR = IP地址 NETMASK = 子网掩码 ONBOOT = 要不要默认启动此接口 GATEWAY = 网关地址 主机名 /etc/sysco

  • linux系统使用python监测网络接口获取网络的输入输出

    net.py 获取网络接口的输入和输出 复制代码 代码如下: #!/usr/bin/env Pythonimport timeimport sys if len(sys.argv) > 1: INTERFACE = sys.argv[1]else: INTERFACE = 'eth0'STATS = []print 'Interface:',INTERFACE def rx(): ifstat = open('/proc/net/dev').readlines() for interface i

  • Linux网络编程之socket文件传输示例

    本文所述示例程序是基于Linux平台的socket网络编程,实现文件传输功能.该示例是基于TCP流协议实现的socket网络文件传输程序.采用C语言编写.最终能够实现传输任何格式文件的文件传输程序. 具体实现代码如下: Server端代码如下: /************************************************************************* > File Name: Server.c > Author: SongLee ***********

  • Linux网络启动问题:Device does not seem to be present解决办法

    Linux网络启动问题:Device does not seem to be present解决办法 在整虚拟机时候经常会遇到虚拟机拷贝,然而拷贝之后网络配置会遇到错误 service network restart启动网络时候提示如下错误: Device does not seem to be present 解决步骤 1.ifconfig -a 查看当前网卡 2.修改网络配置文件, vi /etc/sysconfig/network-scripts/ifcfg-eth0 在原来文件的基础上,

  • Linux网络编程之UDP Socket程序示例

    在网络传输协议中,TCP协议提供的是一种可靠的,复杂的,面向连接的数据流(SOCK_STREAM)传输服务,它通过三段式握手过程建立连接.TCP有一种"重传确认"机制,即接收端收到数据后要发出一个肯定确认的信号,发送端如果收到接收端肯定确认的信号,就会继续发送其他的数据,如果没有,它就会重新发送. 相对而言,UDP协议则是一种无连接的,不可靠的数据报(SOCK_DGRAM)传输服务.使用UDP套接口不用建立连接,服务端在调用socket()生成一个套接字并调用bind()绑定端口后就可

  • Linux下用netstat查看网络状态、端口状态

    在linux一般使用netstat 来查看系统端口使用情况步. netstat命令是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表.实际的网络连接以及每一个网络接口设备的 netstat命令的功能是显示网络连接.路由表和网络接口信息,可以让用户得知目前都有哪些网络连接正在运作. 该命令的一般格式为: netstat [选项] 命令中各选项的含义如下: -a 显示所有socket,包括正在监听的. -c 每隔1秒就重新显示一遍,直到用户中断它. -i 显示所有网络接口的信息,格式同"i

  • Linux 检测服务器是否连接着网络

    Linux 检测服务器是否连接着网络 摘要: 每隔5分钟检测一次服务器是否连接着网络,如果三次检测都没有网络?则自动关机! 主要使用场景: 由于自己有一台服务器放在偏远的老家,有可能会遇到停电导致断网的问题,并且停电后UPS使用时间也有限制, 因此设计此脚本为了解决停电的时候服务器突然断电引起的各种问题,当停电后网络也就不通了,此时需要自动关闭服务器. 当然,来电后需要手动启动服务器!!! #!/bin/bash # 检测服务器是否连接着网络,如果网络不通 则 3次后 关机 # crontab

  • Linux网络编程之基于UDP实现可靠的文件传输示例

    了解网络传输协议的人都知道,采用TCP实现文件传输很简单.相对于TCP,由于UDP是面向无连接.不可靠的传输协议,所以我们需要考虑丢包和后发先至(包的顺序)的问题,所以我们想要实现UDP传输文件,则需要解决这两个问题.方法就是给数据包编号,按照包的顺序接收并存储,接收端接收到数据包后发送确认信息给发送端,发送端接收确认数据以后再继续发送下一个包,如果接收端收到的数据包的编号不是期望的编号,则要求发送端重新发送. 下面展示的是基于linux下C语言实现的一个示例程序,该程序定义一个包的结构体,其中

  • 简析Linux网络编程函数

    目录 1,创建套接字socket 2,绑定套接字bind 3,创建监听:listen 4,等待连接accept 5, 收发消息send和recv 6,关闭套接字描述符close 7,基于tcp协议的C/S服务器模型 8,实现代码 网络编程的一些基本函数:也是实现tcp协议通讯的基本步骤,实现代码在最后,IP需要修改为自己的IP,即可通信: 1,创建套接字socket 函数原型: #include<sys/types.h> #include<sys/socket.h> int soc

  • python网络编程之数据传输UDP实例分析

    本文实例讲述了python网络编程之数据传输UDP实现方法.分享给大家供大家参考.具体分析如下: 一.问题: 你觉得网络上像msn,qq之类的工具在多台机器之间互相传输数据神秘吗?你也想玩一下在两台机器之间传数据吗?今天让python告诉我们基本原理吧,当然只是做简单的了解,实际情况复杂的多.      我们今天用python实现一个简单的udp程序. 二.程序实现: 1) 使用模块 (socket)套接字模块: 套接字模块是一个非常简单的基于对象的接口,它提供对低层BSD套接字样式网络的访问

  • 学习Linux网络编程基本函数

    目录 1,创建套接字socket 函数原型: 参数列表: type的值: protocol得值: 2,绑定套接字bind 函数原型: 参数列表: 3,创建监听:listen 函数原型: 参数列表: 4,等待连接accept 函数原型: 5, 收发消息send和recv 函数原型: 该函数的参数: send的流程: recv的流程: 5,关闭套接字描述符close 函数: 6,基于tcp协议的C/S服务器模型 7,实现代码 服务端: 客户端: 1,创建套接字socket 函数原型: #includ

  • java编程实现基于UDP协议传输数据的方法

    本文实例讲述了java编程实现基于UDP协议传输数据的方法.分享给大家供大家参考,具体如下: UDP协议(User Datagram Protocol,用户数据报协议)不同于TCP协议,它是不可能靠的,但是它比TCP协议具有更快的传输速度,UDP发送的数据单元称为数据报,当网络传输UDP传输UDP数据报是无法保证数据能够到达目的地,也无法保证按发送的顺序到达目的地,也就是说先发送了"hello",再发送了"world",但接收方可能会先收到"world&q

  • Java网络编程之基于TCP协议

    一.单向通信 功能:客户端发送一句话到服务器: 客户端: public class TestClient {//客户端 //这是一个main方法,是程序的入口: public static void main(String[] args) throws IOException { //1.创建套接字:指定服务器的ip和端口号: Socket s = new Socket("192.168.199.217",8888); //2.对于程序员来说,向外发送数据 感受 -->利用输出流

  • 基于socket和javaFX简单文件传输工具

    本文实例介绍了基于socket和javaFX简单文件传输工具,分享给大家供大家参考,具体内容如下 package application; import java.io.File; import org.james.component.ButtonBox; import org.james.component.FileReceiverGrid; import org.james.component.FileSenderGrid; import javafx.application.Applica

  • linux通过跳板机连接远程服务器并进行文件传输的方法

    最近在linux主机上部署环境时,遇到了很多问题,第一个就是通过跳板机远程连接服务器传输文件的问题. 看了很多网上的解决办法,大部分就是说用SecureCRT软件的Alt+P命令,然后通过SFTP进行传输,其中主要涉及以下几个指令 在sftp界面下有几个命令比较重要 cd 主要是打开服务器存放文件的位置 lcd 主要是打开本地待上传文件的位置 put 是上传文件的指令 get 是从服务器下载文件的指令 在sftp界面下有几个命令比较重要 cd 主要是打开服务器存放文件的位置 lcd 主要是打开本

  • linux网络编程用到的网络函数详解用和使用示例

    一.概念介绍网络程序分为服务端程序和客户端程序.服务端即提供服务的一方,客户端为请求服务的一方.但实际情况是有些程序的客户端.服务器端角色不是这么明显,即互为客户端和服务端.我们编写网络程序时,一般是基于TCP协议或者UDP协议进行网络通信的.TCP:(Transfer Control Protocol)传输控制协议是一种面向连接的协议, 当我们的网络程序使用这个协议的时候,网络可以保证我们的客户端和服务端之间的传输是可靠的.UDP:(User Datagram Protocol)用户数据报协议

随机推荐