小项目:TCP实现客户端按文件名传输文件

目录:

1. readme.txt, 该文件描述了各个文件的代码的功能

2.  .info文件, 配置文件, 存放了一些IP和port

3. TCP.h文件

4. INIT.c文件

5. server.c文件

6.client.cw文件

7.Makefile

8.调试调出来的经验总结

一.readme.txt

1. .info文件负责存放一些配置信息
2. TCP.h文件包含一些头文件, 定义的一些数据类型, 一些函数指针和声明的几个函数
3. INIT.c实现了TCP.h文件中声明的那些函数
4. server.c服务端代码
5. client.c客户端代码
6. Makefile ,对代码编译, 简化操作的

二.  .info

127.0.0.1
8888

三. TCP.h

/*防止头文件被重复的包含*/
#ifndef _TCP_
#define _TCP_

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<stdbool.h>
#include<errno.h>
#include <sys/stat.h>
#include <fcntl.h>
/*定义出错处理的宏*/
#define ERR_EXIT(msg) do{perror(msg);exit(EXIT_FAILURE);}while(0)
#define BACKLOG 10            
#define CONFIGFILE ".info"    
#define OK 1
#define CONFIG_SIZE 20
/*定义一些数据类型和函数指针*/
typedef struct sockaddr_in Addr_in;
typedef struct sockaddr Addr;
typedef int (*Fun_p1)(int, struct sockaddr *, socklen_t);
typedef ssize_t (*Fun_p2)(int, void *, size_t, int);
/*声明几个函数*/
void Argument_check(int argc, const char *argv[]);
int SockInit(const char *argv[], bool flags);
void InintConfig(const char *argv[]);
int DataHandler(int fd, void *buf, int data_size, Fun_p2 fp);

#endif 

四. INIT.c文件

#include"TCP.h"

/*@brife:检查参数
 * @para: main function param
 *@return: None
*/
void Argument_check(int argc, const char *argv[]) {
	if(argc < 3) {
		printf("%s a few argument <addr><port>\n", argv[0]);
		exit(0);
	}
}

/*准备套接字, 使用标志位区分执行服务端和客户端的代码*/
int SockInit(const char *argv[], bool flags) {
	int sockfd;
	Fun_p1 fp = flags?bind:connect;
	Addr_in addr;
	socklen_t addrlen = sizeof(addr);
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		ERR_EXIT("socket");
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	if(!(inet_aton(argv[1], &addr.sin_addr))) 
		ERR_EXIT("inet_aton");

	fp(sockfd, (Addr *)&addr, addrlen);

	if(flags) {
		int flags = 1;
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(int));
		if((listen(sockfd, BACKLOG)) < 0)
			ERR_EXIT("listen");
	}

	return sockfd;
}

/*从配置文件中读出IP和port*/
void InintConfig(const char *argv[]) {
	printf("[%s %d]\n", __FUNCTION__, __LINE__);
	FILE *fp;
	char buf[CONFIG_SIZE];
	if((fp = fopen(CONFIGFILE, "r")) == NULL)
		ERR_EXIT("fopen");
	fgets(buf, 20, fp);
	int len = strlen(buf);
	buf[len - 1] = '\0';
	argv[1] = buf;

	fgets(buf, 20, fp);
	len = strlen(buf);
	buf[len - 1] = '\0';   //去掉回车
	argv[2] = buf;
	
	return ;
}

/*数据处理*/
int DataHandler(int fd, void *buf, int data_size, Fun_p2 fp) {
	int ret;
	char *str =fp==recv? "recv":"send";
	do{
		ret = fp(fd, buf, data_size, 0);  
	}while(ret < 0 && errno == EINTR);   //因为信号问题而产生的中断	
	if(ret < 0)
		ERR_EXIT(str);

	return ret;
}

五.  server.c(服务端)

#include"TCP.h"

int main(int argc, const char *argv[])
{
	int fd, newfd, file_fd;
	int ret;
	char buf[BUFSIZ] = {};
	Addr_in peeraddr;
	socklen_t peeraddrlen = sizeof(Addr_in);
	/*检查参数*/
	Argument_check(argc, argv);
	/*准备套接字*/
	fd = SockInit(argv, true);
	
	do{
		newfd = accept(fd, (Addr *)&peeraddr, &peeraddrlen);
	}while(newfd < 0 && errno == EINTR);
	if(newfd < 0)
		ERR_EXIT("accept line 13");
	printf("[%s %d]:connect succes.\n", inet_ntoa(peeraddr.sin_addr), \
			ntohs(peeraddr.sin_port));
	/*接收客户端发来的文件名*/
	ret = DataHandler(newfd, buf, BUFSIZ, recv);
	/*在服务端目录下创建该文件*/
	if((file_fd = open(buf, O_WRONLY|O_CREAT, 0666)) < 0)
		ERR_EXIT("open line 19");
	/*给客户端发送确认的信息, 给客户端一个何时发送数据的判断依据*/
	buf[0] = OK;
	DataHandler(newfd, buf, 1, (Fun_p2)send);
	/*循环处理数据*/
	while(1) {
		ret = DataHandler(newfd, buf, BUFSIZ, recv);	
		if(ret < 0) {
			ERR_EXIT("DataHandler");
		} else if(ret == 0) {
			printf("[%s %d]:disconnect succes.\n", inet_ntoa(peeraddr.sin_addr), \
				ntohs(peeraddr.sin_port));
				break;
		}
		/*将接收到缓存区里的数据写到创建的文件中去*/
		write(file_fd, buf, ret);
	}
	close(newfd);
	close(fd);
	close(file_fd);
	return 0;
}

六. client.cw文件

#include"TCP.h"

int main(int argc, const char *argv[])
{
	int fd, file_fd;
	int ret;
	char buf[BUFSIZ] = {};
	char *filename = (char *)argv[1];
	FILE *fp;
	/*打开配置文件, 读出IP和port给主函数参数*/
	if((fp = fopen(".info", "r")) == NULL)
		ERR_EXIT("fopen");
	fgets(buf, 20, fp);
	buf[strlen(buf) - 1] = '\0';
	argv[1]  = buf;

	fgets(&buf[20], 20, fp);
	buf[strlen(&buf[20]) - 1 + 20] = '\0';
	argv[2]  = &buf[20];
	/*准备套接字*/
	fd = SockInit(argv, false);
	/*发送文件名*/	
	DataHandler(fd, filename, strlen(filename), (Fun_p2)send);
	/*接收确认信号*/
	DataHandler(fd, buf, 1, recv);
	
	/*打开要发送的文件*/
	file_fd = open(filename, O_RDONLY);
	if(file_fd < 0)
		ERR_EXIT("open line 16");
	if(buf[0] == OK) {
		do{
			ret = read(file_fd, buf, BUFSIZ);
		}while(ret < 0 && errno == EINTR);
		if(ret < 0)
			ERR_EXIT("read");
		if(ret > 0) {
			DataHandler(fd, buf, ret, (Fun_p2)send);
		} else if(!ret) {
			printf("over\n");
			exit(0);
		}
	
	}
	close(file_fd);
	close(fd);
	return 0;
}

7.Makefile

CC=gcc
CFLAGS=-g -Wall
all:server client
server:server.c INIT.c
client:client.c INIT.c

clean:
	rm server client

mv:
	mv ./client .info /tmp/

八. 调试调出来的经验总结

1.打开一个文件的时候注意文件的权限,注意区分读写权限的使用场景。

2.编译出现missing terminaing character缺失终止字符, 原因可能是输入了中文的符号或者代码出现换行没加\。

3. 注意写入文件和如读出文件的触发条件。

4.fgets函数使用的很多小坑, 对缓冲区的地址偏移的计算。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
在 Linux 中使用代码实现简单的 TCP 文件传输客户端可以使用 C 语言和 Socket 编程来完成。以下是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define PORT 8080 #define BUFF_SIZE 1024 int main() { int sock = 0, valread; struct sockaddr_in serv_addr; char buffer[BUFF_SIZE] = {0}; FILE *file; // 创建客户端的 socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 将 IP 地址从字符串转换为二进制形式 if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { perror("inet_pton failed"); exit(EXIT_FAILURE); } // 连接服务器端的 socket if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("connect failed"); exit(EXIT_FAILURE); } // 发送文件名服务器端 char *filename = "example.txt"; send(sock, filename, strlen(filename), 0); // 创建新文件以保存接收到的数据 file = fopen("received_file.txt", "wb"); if (file == NULL) { perror("File creation failed"); exit(EXIT_FAILURE); } // 接收并写入文件内容 memset(buffer, 0, sizeof(buffer)); while ((valread = read(sock, buffer, BUFF_SIZE)) > 0) { fwrite(buffer, 1, valread, file); memset(buffer, 0, sizeof(buffer)); } // 关闭连接和文件 close(sock); fclose(file); return 0; } ``` 这个例子演示了一个简单的 TCP 文件传输客户端实现。它创建一个客户端的 socket,并连接到指定的服务器 IP 地址和端口。然后,它发送文件名服务器端,并创建一个新文件用于保存接收到的数据。接下来,它接收服务器发送文件内容,并将内容写入文件中。 请注意,这只是一个简单的示例代码,没有进行错误处理和完整性检查。在实际应用中,你可能需要添加额外的逻辑来处理错误、检查文件是否存在、处理断点续传等功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@daiwei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值