最近学习了Linux下的TCP
网络编程,想实践一下,于是写了利用TCP
协议、传输文件到阿里云服务器的客户端程序和服务端程序。
首先来看看实现的效果。
1. 实现一次传输
虚拟机Ubuntu
的/桌面/TCP_File
目录下,有如下文件:
将其发送到自己的阿里云服务器的/home/www/website/
目录下。
首先在云服务器端运行server
:
Ubuntu系统运行client
,并输入相应的参数(云服务器地址、文件目录/文件名):
按下回车后,各界面上的响应:
-
客户端:
-
服务端
查看服务器端,文件已传输完成:
2. 程序流程
-
客户端
-
服务端
3. 文件传输源代码
1. 客户端client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
// 端口号6666
#define PORT 6666
// 定义buffer大小
#define BUF_SIZE 1024
// #define LISTEN_LENGTH 20
int main(int argc, char *argv[])
{
printf("客户端启动...\n");
// 简单判断用户输入的命令行参数,若不符合规范则给出提示
if (argc != 3)
{
// argv[0]是输入的第一个参数
printf("请输入正确的参数!\n");
printf("格式: %s 服务器主机地址 目录/文件名 \n", argv[0]);
printf("举例: %s 47.110.144.145 XXX.txt \n", argv[0]);
// exit(1)表示异常退出
exit(1);
}
int sockfd, fp;
// int fp;
int recvBytes, sendBytes, readBytes;
// 定义buffer
unsigned char buffer[BUF_SIZE];
// 定义sockaddr_in结构体
struct sockaddr_in serv_addr;
struct hostent *host;
// 打开要发送的文件
// argv[2]是输入的第三个参数
// 打开文件方式为O_RDONLY只读方式
if ((fp = open(argv[2], O_RDONLY)) == -1)
{
printf("打开%s文件失败!\n", argv[2]);
exit(1);
}
printf("打开%s文件成功!\n", argv[2]);
// 解析hostname
// argv[1]是输入的第二个参数
if ((host = gethostbyname(argv[1])) == NULL)
{
printf("hostname错误!\n");
exit(1);
}
// 创建socket流式套接字
// AF_INET允许与远程主机通信
// SOCK_STREAM代表流式套接字,采用TCP协议
// 失败返回-1
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("创建socket失败!\n");
exit(1);
}
printf("创建socket成功!\n");
// 请求连接服务器
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
// 将sin_zero清零
bzero(&(serv_addr.sin_zero), 8);
// 调用connect(),失败返回-1
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
{
printf("连接服务器失败!\n");
exit(1);
}
printf("连接服务器成功!\n");
// ------------------通信阶段------------------
// 1.发送文件名
// 将buffer清零
memset(buffer, 0, sizeof(buffer));
// argv[2]是输入的第三个参数,文件名
strcpy(buffer, argv[2]);
// 调用send()
send(sockfd, buffer, strlen(buffer) + 1, 0);
printf("发送文件名完成!\n");
// 将buffer清零
memset(buffer, 0, sizeof(buffer));
// 调用recv()
recvBytes = recv(sockfd, buffer, sizeof(buffer), 0);
// 2.发送数据
// 将buffer清零
memset(buffer, 0, sizeof(buffer));
printf("开始发送数据...\n");
// 读文件,读取的字节数为buffer的长度
// 成功返回当前读取的字节数
// 如果返回值大于0,说明可能还没读取完毕,则继续执行一次
while ((readBytes = read(fp, buffer, BUF_SIZE)) > 0)
{
// 调用send(),将这次读取的数据发送
// readBytes 为这次读取的字节数
sendBytes = send(sockfd, buffer, readBytes, 0);
// 如果出错,返回-1,给出提示并退出
if (sendBytes < 0)
{
printf("× 发送文件失败!\n");
exit(1);
}
// 将buffer清零
memset(buffer, 0, sizeof(buffer));
}
printf("√ 发送文件成功!\n");
// 关闭文件
close(fp);
// 关闭套接字
close(sockfd);
printf("客户端退出...\n");
}
2. 服务端server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
// 端口号6666
#define PORT 6666
// 定义buffer大小
#define BUF_SIZE 1024
#define Name 100
// 定义listen中请求队列的大小
#define LISTEN_LENGTH 20
void main()
{
// 定义sockaddr_in结构体
struct sockaddr_in server_sockaddr, client_sockaddr;
int size, recvBytes, writeBytes, fp;
int sockfd, listenfd;
int i = 0;
unsigned char filePath[Name], filePath1[Name], filePath2[Name];
// unsigned char filePath1[Name], filePath2[Name];
// 定义buffer
unsigned char buffer[BUF_SIZE];
printf("服务端启动...\n");
// 创建socket流式套接字,失败返回-1
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("socket创建失败!\n");
exit(1);
}
printf("socket创建成功!listenfd=%d\n", listenfd);
// 绑定地址及端口
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = INADDR_ANY;
// 将sin_zero清零
bzero(&(server_sockaddr.sin_zero), 8);
// 调用bind(),失败返回-1
if ((bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr))) == -1)
{
printf("绑定失败!\n");
exit(1);
}
printf("绑定成功!\n");
// 监听客户端请求
// 调用listen(),失败返回-1
if (listen(listenfd, LISTEN_LENGTH) == -1)
{
printf("监听失败!\n");
exit(1);
}
printf("监听连接请求...\n");
// 接收连接
size = sizeof(client_sockaddr);
// 调用accept(),接收请求
sockfd = accept(listenfd, (struct sockaddr *)&client_sockaddr, &size);
if (sockfd == -1)
{
printf("连接失败!\n");
exit(1);
}
printf("连接成功!\n");
// ------------------通信阶段------------------
// 1.传输文件名
// 将buffer清零
bzero(buffer, BUF_SIZE);
// 调用recv(),接受数据保存在buffer
recvBytes = recv(sockfd, buffer, BUF_SIZE, 0);
// 把buffer中的数据复制到filePath
strcpy(filePath, buffer);
// 将buffer清零
memset(buffer, 0, sizeof(buffer));
// 将反馈信息放入buffer
strcpy(buffer, "文件名接收成功!\n");
// 应答
send(sockfd, buffer, strlen(buffer) + 1, 0);
// 2.编辑文件名编辑,去除带'/'的路径
// 判断是否输入了目录
if (strchr(filePath, '/') != NULL)
{
// 输入了目录,作处理,去掉'/'
strcpy(filePath1, strrchr(filePath, '/'));
for (i = 1; filePath1[i] != '\0'; i++)
{
filePath2[i - 1] = filePath1[i];
filePath2[i] = '\0';
}
}
else
// 没有输入目录,直接赋值
strcpy(filePath2, filePath);
// 3.服务端创建文件
// 创建文件,并以读写方式打开
// 0777表示所有者、组成员和其他用户都有读、写和执行权限
fp = open(filePath2, O_CREAT | O_RDWR, 0777);
// 创建失败返回-1
if (fp == -1)
{
printf("%s文件创建失败!\n", filePath2);
exit(1);
}
printf("%s文件创建成功!\n", filePath2);
// 4.传输文件的数据
// 将buffer清零
bzero(buffer, BUF_SIZE);
printf("开始接收数据...\n");
// 调用recv(),成功时返回成功接受的字节个数
// 如果返回值大于0,则可能还没有接收完毕,继续重复执行
// 接受的数据放入buffer
while (recvBytes = recv(sockfd, buffer, BUF_SIZE, 0))
{
// 接收过程出现错误,给出提示
if (recvBytes < 0)
{
printf("接收文件失败!\n");
break;
}
// 将接受的数据写入文件中,写的个数为接受的字节数
// 成功时返回实际写入的数据
writeBytes = write(fp, buffer, recvBytes);
// 实际写入数据 < 接受的数据,则传输错误
if (writeBytes < recvBytes)
{
printf("传输数据失败!\n");
break;
}
// 将buffer清零
bzero(buffer, BUF_SIZE);
}
printf("√ 接收文件成功!\n");
// 关闭文件
close(fp);
// 关闭连接套接字
close(sockfd);
// 关闭监听套接字
close(listenfd);
printf("服务端退出...\n");
}
📘📘欢迎在我的博客上访问:
https://lzxjack.top/