【C/C++学院】(29)网络编程--实现跨平台传输文件(TCP版)

网络编程--实现跨平台传输文件(TCP版)源码下载地址

为了实现跨平台,需要对跨平台的代码进行条件编译。

gcc的-D选项。

连接选项

-lWs2_32

代表要用Ws2_32.lib这个库

gcc编译选项,-D 代表定义一个宏,等同于在c语言当中定义 #defind WIN

在windows下,使用socket之前,必须使用WSAStartup初始化socket,程序运行结束以后必须调用WSACleanup释放相关资源

windown下,关闭socket使用closesocket函数

//makefile-win

.SUFFIXES: .c .o
CC=gcc
SERVERSRCS=server.c            pub.c
CLIENTSRCS=client.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) -lWs2_32
    $(CC) -static -o $(CLIENTEXEC) $(CLIENTOBJS) -lWs2_32
    @echo '-------------ok--------------'
.c.o:
    $(CC) -Wall -DWIN -o [email protected] -c $<
clean:
    rm -f $(SERVEROBJS)
    rm -f $(CLIENTOBJS)
    rm -f core*

//makefile

.SUFFIXES: .c .o
CC=gcc
SERVERSRCS=server.c            pub.c
CLIENTSRCS=client.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 -o [email protected] -c $<
clean:
    rm -f $(SERVEROBJS)
    rm -f $(CLIENTOBJS)
    rm -f core*

//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

#ifdef WIN
#include <WinSock2.h>
#else
#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  //256k

void getfilename(const char *filename, char *name)//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
{
	int len = strlen(filename);
	int i;
	for (i = (len - 1); i >= 0; i--)
	{
		if ((filename[i] == '\\') || (filename[i] == '/'))
		{
			break;
		}
	}
	strcpy(name, &filename[i + 1]);
	return;
}

SOCKET init_socket()//初始化socket
{
//如果是windows,执行如下代码
#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)//连接到指定的主机和端口号
{
	if (init_socket() == -1)
		return 0;

	SOCKET st = socket(AF_INET, SOCK_STREAM, 0);//建立TCP socket
	if (st == 0)
		return 0;
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);//指定port为要连接的端口号
	addr.sin_addr.s_addr = inet_addr(hostname);//指定hostname为要连接的IP地址
	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;//连接成功,返回socket描述符
	}
}

SOCKET socket_create(int port)//在port指定的端口上建立server端socket
{
	if (init_socket() == -1)
		return 0;

	SOCKET st = socket(AF_INET, SOCK_STREAM, 0);//建立TCP socket
	if (st == 0)
		return 0;//如果建立socket失败,返回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 0;
	}
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)//server端socket,所以需要绑定IP地址
	{
		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 %d success\n", port);
	return st;//server端socket建立成功,返回server端socket描述符
}

SOCKET socket_accept(SOCKET listen_st)//server端socket开始accept的函数
{
	struct sockaddr_in client_addr;

#ifdef WIN
	int len = 0;
#else
	unsigned int len = 1;
#endif

	len = sizeof(client_addr);
	memset(&client_addr, 0, sizeof(client_addr));
	SOCKET client_st = accept(listen_st, (struct sockaddr *) &client_addr,
			&len);//accept阻塞,直到有client连接到server才返回
	if (client_st == -1)
	{
		printf("accept failed %s\n", strerror(errno));
		return 0;
	} else
	{
		printf("accept by %s\n", inet_ntoa(client_addr.sin_addr));
		return client_st;//有client连接到server,返回client的socket描述符
	}
}

int send_work(const char *hostname, int port, const char *filename)//向hostname指定的IP地址发送filename指定的文件
{
	SOCKET st = socket_connect(hostname, port);//连接到hostname指定的IP地址和port指定的端口号
	if (st == 0)//连接失败,函数返回
		return 0;

	FILE *fd = fopen(filename, "rb");//以只读方式打开filename指定的文件
	if (fd == NULL)//如果文件打开失败,函数返回
	{
		printf("open %s failed %s\n", filename, strerror(errno));
		return 0;
	}

	char *buf = malloc(BUFSIZE);//申请一个缓冲区,存放接收到的文件内容
	memset(buf, 0, BUFSIZE);
	getfilename(filename, buf);//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
	size_t rc = send(st, buf, strlen(buf), 0);//客户端第一次给server端发送的数据为要传递的文件名称,将解析完成后的文件名通过socket发送给server端
	if (rc <= 0)
	{
		if (rc < 0)
			printf("send failed %s\n", strerror(errno));
		else
			printf("socket disconnect\n");

	} else
	{

		memset(buf, 0, BUFSIZE);
		if (recv(st, buf, BUFSIZE, 0) <= 0)//接收来自server端的回复
		{
			if (rc < 0)
				printf("recv failed %s\n", strerror(errno));
			else
				printf("socket disconnect\n");
		} else
		{
			if (strncmp(buf, "OK", 2) == 0)//如果收到来自服务端的回复,代表服务端认可,可以发送文件了
			{
				while (1)
				{
					memset(buf, 0, BUFSIZE);
					rc = fread(buf, 1, BUFSIZE, fd);//循环读取文件,直到读到文件尾,循环break
					if (rc <= 0)
					{
						if (rc < 0)
							printf("fread failed %s\n", strerror(errno));
						break;
					} else
					{
						rc = send(st, buf, rc, 0);//将从文件中读到的数据,通过socket发送到server端,其中rc为从文件中读到的数据大小
						if (rc <= 0)//如果发送失败,代表TCP连接出错,循环break
						{
							if (rc < 0)
								printf("send failed %s\n", strerror(errno));
							else
								printf("socket disconnect\n");
							break;
						}
					}
				}
			}
		}
	}

	fclose(fd);
	free(buf);

#ifdef WIN
	closesocket(st);
	WSACleanup();
#else
	close(st);
#endif
	return 1;
}

int recv_work(int port)//server端socket在port指定的端口上listen,接收来自client发送的文件
{
	SOCKET listen_st = socket_create(port);//建立server端socket,在port指定端口listen
	if (listen_st == 0)//如果创建服务端socket失败,函数返回0
		return 0;
	SOCKET st = socket_accept(listen_st);//如果有client连接到达,socket_accept函数返回client的socket描述符
	if (st == 0)
		return 0;

	char *buf = malloc(BUFSIZE);//建立接收文件数据缓冲区
	FILE *fd = NULL;

	memset(buf, 0, BUFSIZE);
	size_t rc = recv(st, buf, BUFSIZE, 0);//接收来自client的数据,客户端第一次要发送的文件名称
	if (rc <= 0)
	{
		if (rc < 0)
			printf("recv failed %s\n", strerror(errno));
		else
			printf("socket disconnect\n");

	} else
	{
		printf("receiving %s\n", buf);

		fd = fopen(buf, "wb");//以只写方式打开文件
		if (fd == NULL)
		{
			printf("open %s failed %s\n", buf, strerror(errno));
		} else
		{
			memset(buf, 0, BUFSIZE);
			strcpy(buf, "OK");
			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");
			}

			while (1)
			{
				memset(buf, 0, BUFSIZE);
				rc = recv(st, buf, BUFSIZE, 0);//循环接收来自client的数据,数据为发送文件的内容
				if (rc <= 0)//如果client连接断开,代表文件传递完成,或者网络意外中断,循环break
				{
					if (rc < 0)
						printf("recv 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;
}

//client.c

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

int main(int arg, char *args[])
{
	if (arg < 4)//如果参数小于3个,main函数退出
	{
		printf("usage:client host port filename\n");
		return EXIT_FAILURE;
	}

	int iport = atoi(args[2]);//将第二个参数转化为端口号
	if (iport == 0)//如果端口号为0,main函数退出
	{
		printf("port %d is invalid\n", iport);
		return EXIT_FAILURE;
	}

	printf("%s send begin\n", args[3]);
	if (send_work(args[1], iport, args[3]) == 1)//将第一个参数做为IP地址,第二个参数做为端口号,第三个参数做为要发送的文件名传递给send_work函数
		printf("%s send success\n", args[3]);
	else
		printf("%s send fail\n", args[3]);

	return EXIT_SUCCESS;
}

//server.c

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

int main(int arg, char *args[])
{
	if (arg < 2)//如果没有参数,main函数退出
	{
		printf("usage:server port\n");
		return EXIT_FAILURE;
	}

	int iport = atoi(args[1]);//将第一个参数转化为端口号,server端socket要在这个端口号上listen
	if (iport == 0)
	{
		printf("port %d is invalid\n", iport);
		return EXIT_FAILURE;
	}

	printf("recv is begin\n");
	if (recv_work(iport) == 1)//server端socket在port指定的端口上listen,接收来自client发送的文件
		printf("recv success\n");
	else
		printf("recv fail\n");
	return EXIT_SUCCESS;
}

在window下编译命令,进入到源文件所在路径下,执行:

make -f makefile-win

运行:

一:windows接收,linux发送

二:windows发送,linux接收

时间: 2024-12-15 06:12:21

【C/C++学院】(29)网络编程--实现跨平台传输文件(TCP版)的相关文章

Android网络编程只局域网传输文件

Android网络编程之局域网传输文件: 首先创建一个socket管理类,该类是传输文件的核心类,主要用来发送文件和接收文件 具体代码如下: 1 package com.jiao.filesend; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java

【UNIX网络编程(三)】TCP客户/服务器程序示例

上一节给出了TCP网络编程的函数,这一节使用那些基本函数编写一个完成的TCP客户/服务器程序示例. 该例子执行的步骤如下: 1.客户从标准输入读入一行文本,并写给服务器. 2.服务器从网络输入读入这行文本,并回射给客户. 3.客户从网络输入读入这行回射文本,并显示在标准输出上. 用图描述如下: 编写TCP回射服务器程序如下: #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <st

【UNIX网络编程(四)】TCP套接字编程详细分析

引言: 套接字编程其实跟进程间通信有一定的相似性,可能也正因为此,stevens这位大神才会将套接字编程与进程间的通信都归为"网络编程",并分别写成了两本书<UNP1><UNP2>.TCP套接字编程是套接字编程中非常重要的一种,仔细分析,其实它的原理并不复杂.现在就以一个例子来详细分析TCP套接字编程. 一.示例要求: 本节中试着编写一个完成的TCP客户/服务器程序示例,并对它进行深入的探讨.该示例会用到绝大多数的基本函数,未用到但比较重要的函数会在后面的补充上

网络编程四:互联网中TCP Socket服务器的实现过程需要考虑哪些安全问题

这篇曾经是答在这里的 互联网中TCP Socket服务器的实现过程需要考虑哪些安全问题- auxten 的回答 最近总是有人问我相关的问题,在专栏补发一下,希望能帮到更多人 首先,这是个很大的命题,之前在360负责过几个对外的服务的研发,也算是有点小经验,我试着答一下 在Internet环境下安全问题我主要分为如下几类 1. 信息传输过程中被黑客窃取 2. 服务器自身的安全 3. 服务端数据的安全 首先,如果能用https,就尽量用https,能用nginx等常见服务器,就用常见服务器,主要能避

网络编程的基本概念,TCP/IP协议简介

8.1.1 网络基础知识 计算机网络形式多样,内容繁杂.网络上的计算机要互相通信,必须遵循一定的协议.目前使用最广泛的网络协议是Internet上所使用的TCP/IP协议. 网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯.网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机.而TCP层则提供面向应

arpa/inet.h所引起的Segmentation fault及网络编程常见的头文件

最近在学习Linux网络编程方面的知识,感觉还是有些困难.主要是对协议过程的理解,还有socket的API的理解不够深刻.今天复习编写了一个TCP的服务端和客户端的程序实现client.c从命令行参数中获得一个字符串发给服务器,然后接收服务器返回的已处理的字符串并打印. server.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h>

UNIX网络编程笔记(4)—TCP客户/服务器程序示例

TCP客户/服务器程序示例 这一章信息量开始大起来了,粗略来看它实现了简单的TCP客户/服务器程序,里面也有一些费解的细节. 1.概述 完整的TCP客户/服务器程序示例.这个简单的例子将执行如下步骤的一个回射服务器(这里的回射服务器就是服务简单的把客户端发送的消息返回给客户): 1)客户从标准输入读入一行文本,并写给服务器 2)服务器从网络输入读入这行文本,并回射给客户 3)客户从网络输入读入这行回射文本,并显示在标准输出上 这样实际上就构成了一个全双工的TCP连接. 本章就围绕了这个简单的TC

(1)网络编程的常识 (2)基于tcp协议的编程模型 (3)tcp协议和udp协议的比较 (4)基于udp协议的编程模型

1.网络编程的常识 目前主流的网络通讯软件有:微信.QQ.YY.陌陌.探探.飞信.阿里旺旺.... 在吗? 1.1 七层网络模型(熟悉) 为了保证数据传递的可靠安全等等,ISO(国际标准委员会组织)将数据的传递从逻辑上划分为以下七层: 应用层.表示层.会话层.传输层.网络层.数据链路层.物理层. 在发送数据之前要按照上述七层协议从上到下一层一层进行加包处理,再发送出去; 在接收数据之后要按照上述七层协议从下到上一层一层进行拆包处理,再解析出来: 1.2 常用的协议(熟悉) http协议 - 超文

网络编程,IP地址,TCP 编程

package cn.socket001; //网络编程: /*网络概念 把多台计算机通过物理线路连接起来,就形成了网络.目的在于交换数据和共享信息. 网络通信的三要素 [1]IP地址:唯一标识网络上的每一台计算机.两台计算机之间通信的必备有素 [2]端口号:计算机中应用的标号(代表一个应用程序) 0-1024系统使用或保留端口 常见端口:http:80 stmp: 25 ftp:21 有效端口0-65536,开发者可以的端口是1025-65536之间.一些第三方引用如mysql:3306 or