Linux利用socket实现两台pc之间的数据传输功能,包括windows到linux,TCP/IP实现

Linux利用socket实现两台pc之间的数据传输功能,包括windows到linux

连接选项


-lWs2_32
代表要用Ws2_32.lib这个库




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




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


windown下,关闭socket使用closesocket函数

先看一下linux环境下的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 -g -o $@ -c $<

clean:
	rm -f $(SERVEROBJS)
	rm -f $(CLIENTOBJS)
	rm -f core*


下面是Windows环境下的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.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 $@ -c $<

clean:
	rm -f $(SERVEROBJS)
	rm -f $(CLIENTOBJS)
	rm -f core*

下面看client客户端的发送文件代码

#include 
#include 
#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地址,第二个参数为端口号,第三个参数为需要发送的文件名
		printf("%s send success\n", args[3]);
	else
		printf("%s send fail\n", args[3]);
	return EXIT_FAILURE;
}

然后是server接收端的接受代码

#include 
#include 
#include "pub.h"

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

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

	printf("recv is begin\n");
	if (recv_work(iport) == 1)// 第一个参数为IP地址,第二个参数为端口号,第三个参数为需要发送的文件名
		printf("recv success\n");
	else
		printf("recv fail\n");
	return EXIT_FAILURE;
}

客户端和服务端用到的函数统统写在pub文件里面

#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_ */

#ifdef WIN
#include 
#else
#include 
#include 
#include 
#include 
#include 
#include 
#define SOCKET int
#endif

#include 
#include "pub.h"

#define BUFSIZE 262144 //1024 * 256

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]);
}

int init_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描述符
	}

}

// 连接到hostname指定的IP地址和port指定的端口号
int send_work(const char *hostname, int port, const char *filename)
{
	// 连接到hostname指定的IP地址和port指定的断口
	SOCKET st = socket_connect(hostname, 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);
	size_t rc = send(st, buf, strlen(buf), 0); // 客户端第一次给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端的回复
		{
			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("send failed %s\n", strerror(errno));
							else
								printf("socket disconnect\n");
							break;
						}
					}
				}
			}
		}
	}
	fclose(fd);
	free(buf);
#ifdef WIN
	close(st);
#endif
	return 1;
}

SOCKET socket_create(int port)
{
	if (init_socket() == -1)
		return 0;
	SOCKET st = socket(AF_INET, SOCK_STREAM, 0);
	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 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)
	{
		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开始accept的函数
SOCKET socket_accept(SOCKET listen_st)
{
	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));
	// accept阻塞,知道有client连接到server才返回
	SOCKET client_st = accept(listen_st, (struct sockaddr *)&client_addr, &len);
	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描述符
	}
}


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

	// 如果有client连接到,socket_accept函数返回client的socket
	SOCKET st = socket_accept(listen_st);
	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;
}



你可能感兴趣的:(Linux编程学习)