tcp实现ftp功能

模拟FTP核心原理:客户端连接服务器后,向服务器发送一个文件。文件名可以通过参数指定,服务器端接收客户端传来的文件(文件名随意),如果文件不存在自动创建文件,如果文件存在,那么清空文件然后写入。

知识点

TCP编程流程图

服务器

socket:创建一个用与链接的套接字(用于链接)

bind:绑定自己的ip地址和端口

listen:监听,将主动套接字转为被动套接字

accept:阻塞等待客户端链接,链接成功返回一个用于通信套接字

recv:接收消息

send:发送消息

close:关闭文件描述符

客户端

socket:创建一个套接字

填充结构体:填充服务器的ip和端口

connect:阻塞等待链接服务器

recv/send:接收/发送消息

close:关闭

函数介绍

1.socket

#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

功能:创建套接字文件

参数:

        domain:协议族 ,选择通信方式

                AF_UNIX, AF_LOCAL 本地通信

                AF_INET IPv4 ip和端口

                AF_INET6 IPv6

        type:通信协议-套接字类型

                SOCK_STREAM 流式套接字

                SOCK_DGRAM 数据报套接字

                SOCK_RAW 原始套接字

        protocol:协议 填0,自动匹配底层TCP或UDP等协议。根据type匹配

        系统默认自动帮助匹配对应协议

        传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP

        网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)

返回值:成功。返回同于链接的文件描述符

                失败 -1,更新errno

2、bind

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
  socklen_t addrlen);
 功能:绑定套接字 - ip和端口
 功能:
   sockfd:套接字文件描述符
   addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信结构体由socket第一个参数确定)   

  addrlen:结构体大小
返回值: 成功0
      失败:-1 更新errno
     
 通用结构体:  
    struct sockaddr {
     sa_family_t sa_family;
     char        sa_data[14];
 }
 
 ipv4的通信结构体:
   struct sockaddr_in {
      sa_family_t    sin_family; /*AF_INET */
      in_port_t      sin_port;   /* 端口 */
      struct in_addr sin_addr;   /* ip地址 */
  };
    struct in_addr {
      uint32_t       s_addr;   
    };

本地通信结构体:
   struct sockaddr_un {
       sa_family_t sun_family;               /* AF_UNIX */
       char        sun_path[108];            /* 套接字文件 */
   };

3、listen

int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同时响应客户端请求链接的最大个数,不能写0.
  不同平台可同时链接的数不同,一般写6-8
    (队列1:保存正在连接)
    (队列2,连接上的客户端)
   返回值:成功 0   失败-1,更新errno  

4、accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept(sockfd,NULL,NULL);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
accept()函数返回,返回一个用于通信的套接字文件;
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;
  返回值: 
     成功:文件描述符; //用于通信
失败:-1,更新errno

5、recv

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据 
参数: 
    sockfd: acceptfd ;
    buf  存放位置
    len  大小
    flags  一般填0,相当于read()函数
    MSG_DONTWAIT  非阻塞
返回值: 
   < 0  失败出错  更新errno
   ==0  表示客户端退出
   >0   成功接收的字节个数

6、send

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:
    sockfd:socket函数的返回值
    buf:发送内容存放的地址
    len:发送内存的长度
    flags:如果填0,相当于write();
返回值: 
   < 0  失败出错  更新errno
   >0   成功发送的字节个数

7、connet

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
     sockfd:socket函数的返回值
     addr:填充的结构体是服务器端的;
     addrlen:结构体的大小
返回值 
      -1 失败,更新errno
      正确 0 

项目功能介绍


        1.均有服务器和客户端代码,基于TCP写的。

        2.在同一路径下,将客户端可执行代码复制到其他的路径下,接下来再不同的路径下运行服务器和客户端。

        3.相当于另外一台电脑在访问服务器。

客户端和服务器链接成功后出现以下提示:四个功能


*************** list ************** //列出服务器所在目录下的文件名(除目录不显示)

***********put filename********** //上传一个文件

***********get filename********** //重服务器所在路径下载文件

**************quit*************** //退出(可只退出客户端,服务器等待下一个客户端链接)


client.c  代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //1创建套接字 用于链接
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);

   //填充ipv4的通信结构体   服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8888); //小端转化成大端
    //发送数据的时候必须要将自己的主机字节序转换为网络字节序(即“大端”字节序)
    serveraddr.sin_addr.s_addr = inet_addr("192.168.50.77");
    //点分十进制数串 -> IPv4地址结构(inet_addr)

    //2.请求连接服务器
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)

    {
        perror("connect err.");
        return -1;
    }
    //5.循环的发送消息
    char buf[128] = "";
    int sendbyte;
    while (1)
    {
        printf("input:\n");
        fgets(buf, sizeof(buf), stdin);
        memset(buf, 0, sizeof(buf));
        sendbyte = send(sockfd, buf, sizeof(buf), 0);
        if (sendbyte < 0)
        {
            perror("send err.");
            return -1;
        }
        close(sockfd);
    }

    return 0;
}

server.c 代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char const *argv[])
{
    //1创建套接字 用于链接
    int sockfd, acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);

    //填充ipv4的通信结构体   服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8888); //小端转化成大端
    //发送数据的时候必须要将自己的主机字节序转换为网络字节序(即“大端”字节序)
    serveraddr.sin_addr.s_addr = inet_addr("192.168.50.77");
    //点分十进制数串 -> IPv4地址结构(inet_addr)

    //2.绑定套接字
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)

    {
        perror("bind err.");
        return -1;
    }
    printf("bind ok.\n");

    //3.监听套接字,将主动套接字转为被动套接字
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }
    //4.阻塞等待客户端链接,链接成功返回一个用于通信的文件描述符
    acceptfd = accept(sockfd, NULL, NULL); //不需要关心具体是哪一个客户端
    if (acceptfd < 0)
    {
        perror("accept err.");
        return -1;
    }
    printf("accept=%d\n", acceptfd); //验证:telnet ip 端口
    //5.循环的接受消息
    char buf[128] = "";
    int recvbyte;
    while (1)
    {
        recvbyte = recv(acceptfd, buf, strlen(buf), 0); //相当于read//flag:比read 多个非阻塞的功能,直接返回
        if (recvbyte < 0)
        {
            perror("recv err.");
            return -1;
        }
        else if (recvbyte == 0)
        {
            printf("client exit.\n"); //客户端退出
            break;
        }
        else
        {
            buf[recvbyte] = '\0'; //
            printf("buf:%s\n", buf);
        }
        close(acceptfd);
        close(sockfd);
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值