基于c语言实现socket文件传输

文章目录

  • 一.概述
  • 二.实现源码
    • 2.1 server.c源码实现:
    • 2.2 client.c源码实现:
  • 三.编译及测试

一.概述

  本文基于socket实现server到client的文件传输,测试流程为client向server发起文件下载请求,同时将文件名发送给server;server收到client的请求之后根据文件名到本地目录中查找相应文件,若是找到匹配的文件则使用fread将文件中的内容读出到buffer缓存中,使用scoket send函数发送给client,client收到数据之后将buffer中的内容使用fwrite函数写到文件中。整个过程使用到的基本操作就是:socket网络传输+文件操作。

二.实现源码

2.1 server.c源码实现:

#include   
#include   
#include   
#include   
#include   
#include   
#include
#include

#define PORT 17980						//定义服务器端口号
#define SERVER "192.168.44.129"			//定义服务器的IP地址

#define LENGTH_OF_LISTEN_QUEUE     20  
#define BUFFER_SIZE                4096  
#define FILE_NAME_MAX_SIZE         512  
  
int main(int argc, char **argv)  
{  
	FILE *fp = NULL;
    // set socket's address information   
    // 设置一个socket地址结构server_addr,代表服务器internet的地址和端口  
    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(SERVER);  
    server_addr.sin_port = htons(PORT);  
  
    // create a stream socket   
    // 创建用于internet的流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口  
    int server_socket = socket(PF_INET, SOCK_STREAM, 0);  
    if (server_socket < 0)  
    {  
        printf("Create Socket Failed!\n");  
        exit(1);  
    }  
	printf("Create Socket Success!\n");  
  
	//注意:由TCP套接字状态TIME_WAIT引起在结束本次会话后close立刻开启下次会话会Bind失败。
	//该状态在套接字关闭后约保留 2 到 4 分钟。在 TIME_WAIT 状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。
	//因此下面两句话的加入可以解决这个问题
	int on = 1;
	setsockopt( server_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

    // 把socket和socket地址结构绑定   
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))  
    {  
        printf("Binded success!\n");  
        exit(1);  
    }  
	printf("Create Socket Success!\n");  
	
    // server_socket用于监听   
    if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))  
    {  
        printf("Server Listen Failed!\n");  
        exit(1);  
    }  
	printf("server listen...\n");  
	
	// 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept  
	// 接受此请求,同时将client端的地址和端口等信息写入client_addr中  
	struct sockaddr_in client_addr;  
	socklen_t          length = sizeof(client_addr);  

	// 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中  
	// 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性,可以  
	// 用select()来实现超时检测   
	// accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信  
	// 这里的new_server_socket代表了这个通信通道  
	int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);  
	if (new_server_socket < 0)  
	{  
		printf("Server Accept Failed!\n");  
		exit(1);  
	}  
	printf("listen连接客户端成功,new_server_socket = %d\n",new_server_socket);  
	printf("客户端ip =  %s\n",inet_ntoa(client_addr.sin_addr));
	printf("客户端端口号port = %d\n",ntohs(client_addr.sin_port));
		
    // 服务器端一直运行用以持续为客户端提供服务   
	char buffer[BUFFER_SIZE];  
    while(1)  
    {  
        bzero(buffer, sizeof(buffer));  
		printf("等待客户端发送过来的文件名:\n");
        length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);  
		if(length > 0)
		{
			printf("接收client消息成功:'%s',共%d个字节的数据\n", buffer, length);
		}
        else
        {  
            printf("客户端断开了连接,退出!!!\n");  
            break;  
        }  
        char file_name[FILE_NAME_MAX_SIZE + 1];  
        bzero(file_name, sizeof(file_name));  
        strncpy(file_name, buffer,strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));  
  
        fp = fopen(file_name, "r");  
        if (fp == NULL)  
        {  
            printf("File:\t%s Not Found!\n", file_name);  
        }  
        else  
        {  
			printf("File:\t%s open success!\n", file_name);  
            bzero(buffer, BUFFER_SIZE);  
            int file_block_length = 0;  
			//循环将文件file_name(fp)中的内容读取到buffer中
            while( (file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)  
            {  
                printf("读取到的文件长度file_block_length = %d\n", file_block_length);  
  
                // 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端  
                if (send(new_server_socket, buffer, file_block_length, 0) < 0)  
                {  
                    printf("Send File:\t%s Failed!\n", file_name);  
                    break;  
                }  
				//清空buffer缓存区
                bzero(buffer, sizeof(buffer));  
            }  
            fclose(fp);  			//关闭文件描述符fp
            printf("File:\t%s Transfer Finished!\n", file_name);  
        }  
    }  
	close(new_server_socket);  		//关闭accept文件描述符
    close(server_socket);  			//关闭socket文件描述符
    return 0;  
}  

2.2 client.c源码实现:

//  
// file_client.c  socket传输文件的client端示例程序   
//
#include                         // for sockaddr_in  
#include                          // for socket  
#include                         // for socket  
#include                              // for printf  
#include                             // for exit  
#include                             // for bzero  
#include  
#include
 
#define SERV_PORT 	17980                   //服务器端口
#define SERV_ADDR 	"192.168.44.129"         //服务器ip
char recv_buf[4096];        //定义接收缓存区
char send_buf[4096];        //定义发送缓存区

int main(int argc, char **argv)  
{  
	int ret = -1;
	int sockfd = -1;  //定义网络文件描述符
	FILE *fp = NULL;  //定义文件操作指针
	
	struct sockaddr_in servaddr={0};  //服务器sockaddr_in定义成ipv4类型的服务器ip结构体(ipv6是sockaddr_inv6)

	//1.首先使用socket函数创建网络文件描述符(类似于文件IO中的open函数)
    //函数原型:int socket(int domain, int type, int protocol);   
	sockfd = socket(AF_INET, SOCK_STREAM, 0);   //ipv4,TCP,系统自动选择protocol
	if(sockfd < 0)
	{
		printf("创建socket文件描述符失败\n");
		_exit(-1);
	}
	printf("sockfd=%d\n",sockfd);
	//2.使用connect函数连接服务器
	//函数原型:int connect(int socket, const struct sockaddr *address,socklen_t address_len);
	servaddr.sin_family = AF_INET;             			//定义servaddr的domain地址族为ipv4
	servaddr.sin_port = htons(SERV_PORT);      			//定义servaddr的portnum为SERV_PORT(8010),host to net short
	servaddr.sin_addr.s_addr = inet_addr(SERV_ADDR);  	//定义servaddr的address为SERV_ADDR(192.168.1.23)  person----->u32

    ret = connect(sockfd, (const struct sockaddr *)&servaddr,sizeof(servaddr));
	if(ret < 0)
	{
		printf("客户端connect服务器失败\n");
		_exit(-1);
	}
	printf("客户端connect服务器成功\n");
	
	//下面客户端和服务器互相收发
	while(1)
	{
		//3.使用send函数发生数据
		printf("请输入要发送给服务器的内容: \n");
		scanf("%s", send_buf);  
		if(!strncmp(send_buf,"+++",3))break;	//输入+++客户端断开连接
		fp = fopen(send_buf, "w");  
		if (fp == NULL)  
		{  
			printf("File:\t%s Can Not Open To Write!\n", send_buf);  
			_exit(-1);  
		}  
		printf("File:\t%s Open Success,Waitting To Write...\n", send_buf); 
		
		ret = send(sockfd, send_buf, strlen(send_buf), 0);
		printf("send buffer:%s,sned len:%d\n",send_buf,ret);
		
		//4.使用recv函数接收来自服务端的消息
		ret = recv(sockfd, recv_buf, sizeof(recv_buf), 0);   
		if(ret < 1)
		{
			printf("服务器断开了连接\n");
			break;
		}
		printf("收到服务器发送的数据:recv buffer:\n%s\nrecv len:%d\n",recv_buf,ret);
		printf("将接收到的数据写入文件中:\n");
		//调用fwrite函数将recv_buf缓存中的数据写入文件中
		int write_length = fwrite(recv_buf, sizeof(char), ret, fp);  
        if (write_length < ret)  
        {  
            printf("文件写入失败!\n");  
            break;  
        }  
		printf("Recieve File:\t %s From Server[%s] 接收成功!\n", send_buf, SERV_ADDR);  
		memset(send_buf,0,sizeof(send_buf));   //清空接收缓存区
		memset(recv_buf,0,sizeof(recv_buf));   //清空接收缓存区
		fclose(fp);  
	}
	printf("关闭连接并退出\n");
	close(sockfd);     //关闭socket文件描述符
	return 0;
}  

三.编译及测试

编译:

gcc send_file_server.c -o server
gcc send_file_client.c -o client

测试:
1.在sever机器上与server同路径下创建test.txt文件并随意写入内容;
2.服务端启动server服务端程序如下:
./server
基于c语言实现socket文件传输_第1张图片
3.客户端启动client服务端程序如下:
./client
基于c语言实现socket文件传输_第2张图片
4.客户端发送需要下载的文件(如test.txt):
基于c语言实现socket文件传输_第3张图片
5.server服务端的响应动作如下:
基于c语言实现socket文件传输_第4张图片

你可能感兴趣的:(计算机网络,socket,文件传输,文件下载,C语言)