linux网络编程之实现跨平台PC之间进行文件传输

//实现两台计算机进行文件传输,包括跨平台传输即linux与windows之间
//TCP的文件传输
//文件大小不限制,需要做到将大文件分包去做

server.c

//服务端程序
int main(int arg,char * args[ ])
{
	if(arg < 2)
	{
		printf("scanf parameter failed\n");
		return 0;
	}
	int port = itoi(args[1]);
	if(port == 0)
	{
		printf("port %d is invalid\n ",port);
		return 0;
	}
	printf("receive begin\n");
	if(recv_work(port) == 1)
		printf("receive success\n");
	else
		printf("receive failed\n");
	return 0;
}

client.c

//实现两台计算机进行文件传输,包括跨平台传输即linux与windows之间
//TCP的文件传输
//文件大小不限制,需要做到将大文件分包去做

//编译时传入的参数是包括:服务器IP地址、端口号、传送的文件名
//client端
int main(int arg,char * args[ ])
{
	//先是判断传入的参数个数
	if(arg < 4)
	{
		printf("scanf parameter failed\n");
		return 0;
	}
	//判断端口号
	int port = atoi(args[2]);
	if(port == 0)
	{
		printf("port %d is invalid\n ",port);
		return 0;
	}
	printf("%s send begin\n",args[3]);
	//将调用一个send_work函数传入服务器IP地址、端口号、传送的文件名
	if(send_work(args[1],port,args[3]) == 1)
	{
		sleep(3);
		printf("%s send success\n",args[3]);
	}
	else
	{
		printf("%s send failed\n",args[3]);
	}
	return EXIT_SUCCESS;
}

pub.h

#ifndef PUB_H_
#define PUB_H_

int send_work(const char * hostname,int port,const char * filename);
int recv_work(int port);

#endif /* PUB_H_ */

pub.c

#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define SOCKET int
#endif

#include <stdio.h>
#include "pub.h"

#define BUFSIZE 262144 //1024 * 256

//为什么要定义一个宏,解释一下,因为此程序可以保证在linux与windows系统下均可运行。
//#ifdef WIN
//#include <WinSock2.h>
//#else 为什么这样写呢,在windows下socket被封装在此头文件下,而且套接口的类型为SOCKET,而linux下的套接口为int型,故作此标记

int init_socket()
{
	//如果是windows系统,执行如下代码,linux系统直接略过此代码
#ifdef WIN
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(1, 1);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return -1;
	}

	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
	{
	   WSACleanup();
	   return -1;
	}
#endif
	return 0;
}
SOCKET socket_connect(const char * hostname,int port)//连接IP地址和port端口号
{
	if(init_socket() == -1)//因为linux与windows系统下使用socket有区别,windows下必须使用此函数初始化(同样用到宏)
		return 0;
	SOCKET st = socket(AF_INET,SOCK_STREAM,0);
	if(st == 0)
		return 0;
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(hostname);
	if(connect(st,(struct sockaddr *) &addr,sizeof(addr)) == -1)
	{
		printf("connect to %s:%d failed %s\n", hostname, port, strerror(errno));
		return 0; // 连接失败,返回0
	}
	else
	{
		return st;
	}
}
void getfilename(const char * filename, char * name)
{
	int len = strlen(filename);
	int i;
	for(i = (len -1);i >= 0;i--)
	{
		if((filename[i] == '\\') ||(filename[i] == '/'))
				{
					break;
				}
	}
	strcpy(name,&filename[i + 1]);
}

//连接到hostname指定的IP地址和port端口号
int send_work(const char * hostname,int port,const char * filename)
{
	SOCKET st = socket_connect(hostname,port);
	if(st == 0)
			return 0;
	//连接成功后,需要将传送的文件打开
	FILE * fd = fopen(filename,"rb");
	if(fd == NULL)//判断文件描述符,看文件是否打开
	{
		printf("open %s failed\n",filename);
		return 0;
	}
	char *buf = malloc(BUFSIZE) ;//申请一个缓冲区,将文件数据放置在此
	memset(buf,0,BUFSIZE);
	//将完整的文件名解析出来
	getfilename(filename,buf);
	//将解析出来的文件名先发送给服务端,等待服务端接收,若接收成功并返回一个信号给客户端
	int rc = send(st,buf,strlen(buf),0);
	if(rc <= 0)//在判断一下是否成功发出
	{
		if(rc < 0)
			printf("send failed %s\n", strerror(errno));
		else
			printf("socket disconnect\n");
	}
	else
	{
		memset(buf,0,sizeof(buf));
		//等待服务器端传来的消息
		if(recv(st,buf,BUFSIZE,0) <= 0)
		{
			printf("socket disconnect\n");
		}
		else
		{
			if(strncmp(buf,"OK",2) == 0)
			{
				while(1)
				{
					memset(buf,0,BUFSIZE);
					rc = fread(buf,1,BUFSIZE,fd);
					if(rc <= 0)
					{
						if (rc < 0)
						   printf("fread failed %s\n", strerror(errno));
						break;
					}
					else
					{
						rc = send(st,buf,rc,0);
						if(rc <= 0)
						{
							if (rc < 0)
								 printf("fread failed %s\n", strerror(errno));
							else
								printf("socket disconnect\n");
							break;
						}
						printf("我已发送\n");
					}
				}
			}
		}
	}
	fclose(fd);
	free(buf);
#ifdef WIN
	closesocket(st);
	WSACleanup();
#else
	close(st);
#endif

	return 1;
}
SOCKET socket_create(int port)
{
	SOCKET st = socket(AF_INET,SOCK_STREAM,0);//初始化socket
	if(st == 0)
		return 0;
#ifdef WIN
	const char on = 0;
#else
	int on = 0;
#endif
	if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
	{
		printf("setsockopt failed %s\n", strerror(errno));
		return EXIT_FAILURE;
	}
	struct sockaddr_in addr;//定义一个IP地址的结构
	memset(&addr,0,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8080);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	//服务器端程序,需要使用bind将IP与server程序绑定
	if(bind(st,(struct sockaddr *) &addr,sizeof(addr)) == -1)
	{
		printf("bind failed %s\n", strerror(errno));
		return 0;
	}
	if(listen(st,20) == -1)
	{
		printf("listen failed %s\n", strerror(errno));
		return 0;
	}
	printf("listen success\n");
	return st;
}

// server端socket开始accept的函数
SOCKET socket_accept(SOCKET client_st)
{
	struct sockaddr_in client_addr;
	memset(&client_addr,0,sizeof(client_addr));
#ifdef WIN
    int len = 0;
#else
    unsigned int len = 1;
#endif
    len = sizeof(client_addr);
    SOCKET listen_st = accept(client_st,(struct sockaddr *) &client_addr,&len);//阻塞调用
    if (listen_st == -1)
    {
    	printf("accept failed %s\n", strerror(errno));
    	return EXIT_FAILURE;
    }
    else
    {
    	printf("accept by %s\n",inet_ntoa(client_addr.sin_addr));
    	return listen_st;
    }

}

// server端socket在port指定的断口上listen,接收来自client发送的文件
int recv_work(int port)
{
	SOCKET st = socket_create(port);// 建立server端socket,在port指定的端口listen
	if(st == 0)
		return 0;
	// 如果有client连接到,socket_accept函数返回client的socket
	SOCKET listen_st = socket_accept(st);
	if(listen_st == 0)
		return 0;
	char *buf = malloc(BUFSIZE);
	memset(buf,0,BUFSIZE);
	FILE * fd = NULL;
	size_t rc = recv(listen_st,buf,BUFSIZE,0);//接收来自client的数据,客户端第一次要发送的文件名称
	if(rc <= 0)
	{
		if(rc < 0)
			printf("receive failed %s\n", strerror(errno));
		else
			printf("socket disconnect\n");
	}
	else
	{
		printf("receicing %s\n",buf);
		fd = fopen(buf,"wb");//以只写的方式打开
		if(fd == NULL)
		{
			printf("open %s failed %s\n", buf, strerror(errno));
			return 0;
		}
		else
		{
			memset(buf,0,BUFSIZE);
			strcpy(buf,"OK");
			rc = send(listen_st,buf,strlen(buf),0);
			if(rc <= 0)
			{
				if(rc < 0)
					printf("send failed %s\n", strerror(errno));
				else
					printf("socket disconnect\n");
			}
			while(1)
			{
				memset(buf,0,BUFSIZE);
				rc = recv(listen_st,buf, BUFSIZE, 0);
				if(rc <= 0)// 如果client连接断开,代表文件传递完成,或者网络意外中断,循环break
				{
					if(rc < 0)
						printf("receive failed %s\n", strerror(errno));
					else
						printf("socket disconnect\n");
					break;
				}
				else
				{
					fwrite(buf,1,rc,fd);// 将从client端收到的内容写入文件
				}
			}
		}
	}
	if(fd)
		fclose(fd);
	free(buf);
#ifdef WIN
    closesocket(st);
    closesocket(listen_st);
    WSACleanup();
#else
    close(st);
    close(listen_st);
#endif
	return 1;
}

在windows下的makfile代码:

.SUFFIXES:.c .o  
  
CC=gcc  
SERVERSRCS=file_server.c\  
	pub.c  
CLIENTSRCS=file_socket.c\  
	pub.c
  
SERVEROBJS=$(SERVERSRCS:.c=.o)  
CLIENTOBJS=$(CLIENTSRCS:.c=.o)  
SERVEREXEC=server.exe  
CLIENTEXEC=client.exe
  
all: $(SERVEROBJS) $(CLIENTOBJS)  
	$(CC) -static -o $(SERVEREXEC) $(SERVEROBJS)  
	$(CC) -static  -o $(CLIENTEXEC) $(CLIENTOBJS)  
	@echo '----------------ok----------------'  
  
.c.o:  
	$(CC) -Wall -DWIN  -g -o $@ -c $<  
  
clean:  
	rm -f $(SERVEROBJS)  
	rm -f $(CLIENTOBJS)  
	rm -f core* 

linux下的makefile文件:

.SUFFIXES:.c .o  
  
CC=gcc  
SERVERSRCS=file_server.c\  
	pub.c  
CLIENTSRCS=file_socket.c\  
	pub.c
  
SERVEROBJS=$(SERVERSRCS:.c=.o)  
CLIENTOBJS=$(CLIENTSRCS:.c=.o)  
SERVEREXEC=server  
CLIENTEXEC=client  
  
all: $(SERVEROBJS) $(CLIENTOBJS)  
	$(CC) -o $(SERVEREXEC) $(SERVEROBJS)  
	$(CC) -o $(CLIENTEXEC) $(CLIENTOBJS)  
	@echo '----------------ok----------------'  
  
.c.o:  
	$(CC) -Wall -g -o $@ -c $<  
  
clean:  
	rm -f $(SERVEROBJS)  
	rm -f $(CLIENTOBJS)  
	rm -f core* 


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值