1. 程式人生 > >Linux利用socket實現兩臺pc之間的資料傳輸功能,包括windows到linux,UDP實現

Linux利用socket實現兩臺pc之間的資料傳輸功能,包括windows到linux,UDP實現

makefile 和TCP一樣,參見上一篇博文

客戶端和服務端主函式也和上一篇的tcp是一樣的,同樣參考上一篇博文,這裡只是修改了pub.c的檔案了

/*
 * pub.c
 *
 *  Created on: 2016年7月14日
 *      Author: Administrator
 */

#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 //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 create_send_socket()// 建立傳送資料的udp socket
{
	if (init_socket() == -1)
		return 0;

	SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);// 建立UDP socket
	if (st == 0)
		return 0; // 如果建立socket失敗,返回0
	return st; // udp socket建立成功,返回socket描述符
}

SOCKET create_recv_socket(int port)
{
	SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);// 建立UDP 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);
	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;
	}
	return st;// udp
}

// 連線到hostname指定的IP地址和port指定的埠號
int send_work(const char *hostname, int port, const char *filename)
{
	SOCKET st_send = create_send_socket();// 建立傳送資料的UDP Socket
	SOCKET st_recv = create_recv_socket(port + 1);// 建立接受資料的UDP Socket
	if (st_send == 0) // 建立失敗,函式返回
	{
		return 0;
	}

	if (st_recv == 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);

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(hostname);

	// 客戶端第一次給server端傳送檔名
	size_t rc = sendto(st_send, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr)); // 客戶端第一次給server端傳送的資料為要傳遞的檔名
	if (rc <= 0)
	{
		printf("send failed %s\n", strerror(errno));
	}
	else
	{
		struct sockaddr_in client_addr;

#ifdef WIN
		int len = 0;
#else
		unsigned int len 0;
#endif
		len = sizeof(client_addr);
		memset(&client_addr, 0, sizeof(client_addr));

		memset(buf, 0, BUFSIZE);
		if (recvfrom(st_recv, buf, BUFSIZE, 0,
				(struct sockaddr *)&client_addr, &len) <= 0) // 接受來自server端的回覆
		{
			printf("recv failed %s\n", strerror(errno));
		}
		else
		{
			if (strncmp(buf, "OK", 2) == 0)
			{
				while(1)
				{
					memset(buf, 0, BUFSIZE);
					rc = fread(buf, 1, BUFSIZE, fd);
					if (rc <= 0)
					{
						break;//
					}
					else
					{
						rc = sendto(st_send, buf, rc, 0, (struct aockaddr *)&addr, sizeof(addr));
						if (rc <= 0)
						{
							printf("send failed %s\n", strerror(errno));
							break;
						}
					}
				}
			}
			memset(buf, 0, BUFSIZE);
			// 連發128位元組0,代表檔案傳送完畢
			rc = sendto(st_send, buf, 128, 0, (struct aockaddr *)&addr, sizeof(addr));
		}
	}
	fclose(fd);
	free(buf);
#ifdef WIN
	closesocket(st_send);
	closesocket(st_recv);
	WSACleanup();
#else
	close(st_send);
	close(st_recv);
#endif
	return 1;
}

// server端socket在port指定的斷口上listen,接收來自client傳送的檔案
int recv_work(int port)
{
	SOCKET st_send = create_send_socket();// 建立傳送資料的UDP Socket
	SOCKET st_recv = create_recv_socket(port);// 建立接受資料的UDP Socket
	if (st_send == 0) // 建立失敗,函式返回
	{
		return 0;
	}

	if (st_recv == 0) // 建立失敗,函式返回
	{
		return 0;
	}

	char *buf = malloc(BUFSIZE);
	FILE *fd = NULL;

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

	struct sockaddr_in client_addr;
	len = sizeof(client_addr);
	memset(&client_addr, 0, sizeof(client_addr));
	memset(buf, 0, BUFSIZE);
	// 接收來自client的資料,客戶端第一次傳送的檔名
	size_t rc = recvfrom(st_recv, buf, BUFSIZE, 0, (struct sockaddr *)&client_addr, &len);

	if (rc <= 0)
	{
		printf("recv failed %s\n", strerror(errno));
	}
	else
	{
		printf("receiving %s\n", buf);

		fd = fopen(buf, "wb");// 以只寫方式開啟檔案
		if (fd == NULL)
		{
			printf("open %s failed %s\n", buf, strerror(errno));
		}
		else
		{
			client_addr.sin_port = htons(port + 1);
			memset(buf, 0, BUFSIZE);
			strcpy(buf, "OK");
			rc = sendto(st_send, buf, strlen(buf), 0, (struct *)&client_addr, sizeof(client_addr)); // 回覆客戶端,同意接收檔案
			if (rc <= 0)
			{
				printf("send failed %s\n", strerror(errno));
			}
			while (1)
			{
				memset(buf, 0, BUFSIZE);
				rc = recvfrom(st_recv, buf, BUFSIZE, 0, (struct *)&client_addr, &len);// 迴圈接收來自client的資料,資料為傳送檔案的內容

				char tmp[128];
				memset(tmp, 0, sizeof(tmp));
				if (memcmp(buf, tmp, sizeof(tmp)) == 0)
				{
					break;
				}
				if (rc <= 0)// 如果client連線斷開,代表檔案傳遞完成,或者網路意外中斷,迴圈break
				{
					printf("recv failed %s\n", strerror(errno));
					break;
				}
				else
				{
					fwrite(buf, 1, rc, fd);// 將從client端收到的內容寫入檔案
				}
			}
		}
	}
	if (fd)
		fclose(fd);
	free(buf);
#ifdef WIN
	closesocket(st_send);
	closesocket(st_recv);
	WSACleanup();
#else
	close(st_send);
	close(st_recv);
#endif
	return 1;
}