C++中的Socket编程使用协议发送图片

使用:
(1)首先运行服务端,待服务端运行起来;
(2)最后运行客户端,输入要传输文件到哪个目标机器的IP地址;
(3)输入传输文件的路径及文件(完成的路径),其中包含文件的类型,也就是后缀需要包含(代表需要传输文件的类型)。
例如:E:/Data(D)/Cat_and_Dog/dog.jpg
参考博主:https://blog.csdn.net/sinat_23118323/article/details/71024351

1.客户端:

#pragma once
#ifndef _TCPSOCKET_H_
#define _TCPSOCKET_H_
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <process.h>
#include<mutex>
#include<string>
#include<atlimage.h>
#include<process.h>
#define MAX 1024*10
#define err(errMsg) printf("[error] %s failed,code %d\
line:%d\n",errMsg, WSAGetLastError(),__LINE__)
#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件

using namespace std;
struct Potocol {
    int length;
};
struct LogicHeader {
    int type;
    int logic_raw_size;
    int log_raw_offset;
};
struct image {
    int width;
    int height;
    int depth;
    char data[0];
};
struct imageCli {
    Potocol pl;
    LogicHeader lh;
    image images;
};
class Socket {
private:
    SOCKET clientSock;
    int id;
    string ip;
    char wb_file[MAXBYTE];
    unsigned long long g_fileSize;
public:
    //获取ClientSock
    SOCKET Getcientsock();
    //获取当前日期
    void OBTION_TIME();
    //获取开始时间
    double START_TIME();
    //获取结束时间
    double END_TIME();
    //获取文件大小
    void  getByteSize(unsigned long long size);
    //返回以MB为单位的文件大小
    unsigned long long RETURN_MB(unsigned long long size);
    //判断输入IP是否存在
    bool TARGE_FILE(string ip);
    //输入文件
    void SEND_FILE(string file);
    //LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
    static DWORD WINAPI transmmit(const LPVOID arg);
    bool INPUT_IP(string ipt);
    //系统的实现以上函数
    int MAIN_SOCKET();
    //获取图片的高度
    int GetHeight(char buf[MAXBYTE]);
    //获取图片的宽度
    int GetWidth(char buf[MAXBYTE]);
    //清理和关闭网络库
    void CLEAR();
   //有裸数据的情况下
    void send_image(int width,int height,int depth,char *data, int length);
    //接收图片
    static DWORD WINAPI run(const LPVOID arg);
    //接收图片的线程
    void Thread();
    //一次性发送的文件
    bool send_once(char*Buffer,int length);
    //接收文件
    int Socket_Recv();
    string TYPE_file();
    void REVER_file(string file1, string file2);
};
//定义结构体用来设置
typedef struct my_file
{
    SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信
    sockaddr_in clientAddr; //用于保存客户端的socket地址
    int id; //文件块的序号
}F;

#endif // !_TCPSOCKET_H_



#include "tcpSocket.h"
#define MAXBYTES 300*1024
//表示一秒钟会有多少个时钟计时单元
#define CLOCKS_PER_SEC ((clock_t)1000) 
mutex m;
//获取当前日期
void Socket::OBTION_TIME() {
	SYSTEMTIME start; //windows.h中  
	GetLocalTime(&start);//time.h的tm结构体一样的效果  
	cout << start.wYear << "/" << start.wMonth << "/" << start.wDay << " " << start.wHour << ":" << start.wMinute << ":" << start.wSecond << endl;
}
// 获取开始时间
double Socket::START_TIME() {
	DWORD start_time;
	start_time = GetTickCount64();
	return (double)start_time;
}
//获取结束时间
double Socket::END_TIME() {
	DWORD end_time;
	end_time = GetTickCount64();
	return double(end_time);
}
SOCKET Socket::Getcientsock() {
	return clientSock;
}
//获取文件大小
void Socket::getByteSize(unsigned long long size) {
	unsigned long long rest = 0;
	if (size < 1024) {
		cout << size << "B" << endl;
		return;
	}
	else {
		size /= 1024;
	}
	if (size < 1024) {
		cout << size << "KB" << endl;
		return;
	}
	else {
		rest = size % 1024;
		size /= 1024;
	}
	if (size < 1024) {
		size *= 100;
		cout << (size / 100) << "." << (rest * 100 / 1024 % 100) << "MB" << endl;
		return;
	}
	else {
		size = size * 100 / 1024;
		cout << (size / 100) << "." << (size % 100) << "GB" << endl;
		return;
	}
}

void Socket::SEND_FILE(string file) {
	int i = 0;
	char Temporary_file[MAXBYTE] = { 0 };//保存发送文件的格式
	for (i = 0; i < file.length(); i++) {
		wb_file[i] = file[i];
		Temporary_file[i] = file[i];
	}
	wb_file[i] = '\0';
	Temporary_file[i] = '\0';
	send(clientSock, Temporary_file, strlen(Temporary_file), 0);
	struct _stat64 st;
	_stat64(wb_file, &st);
	g_fileSize = st.st_size;
}
//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
DWORD WINAPI Socket::transmmit(const LPVOID arg) {
	//上锁是为了方便看输出
	m.lock();

	//F* temp = (F*)arg;
	Socket* so = (Socket*)arg;
	/*
	获取文件的序号int file_id = temp->id;
	获取客户机的端口号ntohs(temp -> clientAddr.sin_port);
	*/
	cout << "测试开始,等待服务端发送消息..." << endl;
	//从客户端处接受数据
	/*
		char Buffer[MAXBYTE] = { 0 }; //缓冲区
		recv(temp->clientSocket, Buffer, MAXBYTE, 0); //recv方法 从客户端通过clientScocket接收
		cout << "线程" << temp->id << "从客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口收到:" << Buffer << endl;
	*/
	char* file_name; //文件路径
	char File_Alias[100] = { 0 };
	file_name = so->wb_file;
	unsigned long long len_file = 0;
	FILE* fp = fopen(file_name, "rb");

	if (fp == NULL) {
		cout << "文件" << file_name << "出错或不存在" << endl;
		
	}
	else {
		/*
		获取文件大小
		注意这个地方不能使用unsigned long long,因为当文件传输很大的时候,ftell返回的是long
		fseek(fp, 0, SEEK_END);//将读取的文件指针放到文件末尾
		g_fileSize = ftell(fp);
		fseek(fp, 0, SEEK_SET);//指针移到文件开头*/

		
		string send_file_len;
		send_file_len = to_string(so->g_fileSize);
		send(so->clientSock, send_file_len.c_str(), send_file_len.length(), 0);
		cout << "发送文件时间: ";
		so->OBTION_TIME();
		double start_time = so->START_TIME();
		char Buffer[MAXBYTES] = { 0 }; //文件缓冲区
		unsigned long long  size = 0; //读取的文件长度
		
		while ((size = fread(Buffer, sizeof(char), MAXBYTES, fp)) > 0) {
			
			//返回非0值表示send错误
			if (send(so->clientSock, Buffer, (unsigned long long)size, NULL) < 0){
				cout << "传输出错,请检查网络配置。" << endl;
				break;
			}
			len_file += size;
			cout.width(3);//i的输出为3位宽
			if ((len_file * 100 / so->g_fileSize) % 5 > 0) {
				cout << (len_file * 100 / so->g_fileSize) << "%";
				cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
			}
			
			size = 0;
			//每次读取完之后清空缓存区,以便下一块文件读入
			memset(&Buffer, 0, MAXBYTES);
		}
		const char* t = "end";
		send(so->clientSock, t, strlen(t), NULL);
		cout << so->id << "线程已成功发送" << file_name << endl;
		
		cout << "发送文件大小: ";
		so->getByteSize(len_file);
		cout << "文件发送结束时间: ";
		so->OBTION_TIME();
		double end_time = so->END_TIME();
		double currentTime = 0;
		currentTime = (double)(end_time - start_time) / CLOCKS_PER_SEC;
		cout << "发送文件耗时: " << currentTime << "s" << endl;
		fclose(fp);
	}
	/*
		发送简单的字符串到客户端
		const char* s = "Server file";
		send(temp->clientSocket, s, strlen(s)*sizeof(char)+1, NULL);
		cout << "线程" << temp->id << "通过客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口发送:" << s << endl;
	*/

	m.unlock();

	return 0;
}
bool Socket::INPUT_IP(string ipt) {
	//客户端socket
	//加载winsock库
	WSADATA wsadata;
	//WSA-windows socket ansyc windows的异步套接字 2.2版本的

	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {
		err("WSAStartup");
		return 0;
	}
	clientSock = socket(AF_INET, SOCK_STREAM, 0);
	if (clientSock == INVALID_SOCKET) {
		err("SOCKET");
		return 0;
	}
	//初始化socket信息
	sockaddr_in clientAddr;
	memset(&clientAddr, 0, sizeof(SOCKADDR));
	//clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	const char* ips = ipt.c_str();
	clientAddr.sin_family = AF_INET;
	clientAddr.sin_port = htons(3725);//将本地字节序转换为网络字节序,大端和小端存储
	clientAddr.sin_addr.s_addr = inet_addr(ips);
	if (connect(clientSock, (SOCKADDR*)&clientAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		err("connect");
		return false;
	}
	return true;
}
//获取图片的高度
int Socket::GetHeight(char buf[MAXBYTE]) {
	LPCTSTR srcFilepath = _T(buf);
	CImage srcImage;
	srcImage.Load(srcFilepath);
	return srcImage.GetHeight();
}
//获取图片的宽度
int Socket::GetWidth(char buf[MAXBYTE]) {
	LPCTSTR srcFilepath = _T(buf);
	CImage srcImage;
	srcImage.Load(srcFilepath);
	return srcImage.GetWidth();
}
//有裸数据的情况下
void Socket::send_image(int width, int height, int depth, char* data, int length) {
	char* buf = new char[sizeof(imageCli) + length+10];
	imageCli& image = *(imageCli*)buf;
	image.images.width = width;
	image.images.height = height;
	image.images.depth = depth;
	/*
	* 测试读取的文件是否正确
	FILE* fp = fopen("D:\\5.jpg", "wb");
	if (fp == NULL) {
		fp = fopen("D:\\5.jpg", "w");
	}
	fwrite(data, sizeof(char), length, fp);
	fclose(fp);
	*/
	image.lh.type = 1;
	image.lh.logic_raw_size = length;
	image.lh.log_raw_offset = sizeof(image.lh) + sizeof(image.images);
	image.pl.length = image.lh.logic_raw_size + image.lh.log_raw_offset;
	memcpy(buf + image.lh.log_raw_offset+4, data, length);
	send(clientSock, buf, image.pl.length + sizeof(image.pl)+5, 0);
	delete []buf;
	
}
//一次性发送的文件
bool Socket::send_once(char* Buffer, int length) {
	int byteReceivedOnce = 0;
	int bytesReceivedAll = 0;
	while (bytesReceivedAll < length) {
		if ((byteReceivedOnce = send(clientSock, Buffer + bytesReceivedAll, length - bytesReceivedAll, NULL)) < 0) {
			return false;
		}
		bytesReceivedAll += byteReceivedOnce;
	}
	return true;
}
//接收和传输文件目录
bool Socket::TARGE_FILE(string ip) {
	bool flag = INPUT_IP(ip);
	if (flag == true)return 1;
	else {
		return 0;
	}
}
DWORD WINAPI Socket::run(const LPVOID arg) {
	Socket* soc = (Socket*)arg;
	char* img;
	while (true) {
		int ret = recv(soc->clientSock, img, strlen(img), NULL);
	}
}
void Socket::Thread() {
	CreateThread(NULL, 0, &run, this, 0, NULL);
}

int Socket::MAIN_SOCKET() {
	//建立连接
	//while (true) {
	cout << "已建立连接。" << endl;
	char Buffer[MAXBYTE] = { 0 }; // 文件缓冲区
	char wb_file[100] = { 0 }; //写入的文件
	//“句柄” 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;
	HANDLE hThread[2];
	for (int i = 0; i < 1; i++) {
		sockaddr_in clntAddr;
		memset(&clntAddr, 0, sizeof(SOCKADDR));
		//使用 API 的 CreateThread, 它执行完入口函数后会自动退出, 无需 ExitThread;
		hThread[i] = CreateThread(NULL, 0, &transmmit, this, 0, NULL);
	}
	//等待子线程完成
	WaitForMultipleObjects(1, hThread, TRUE, INFINITE);
	cout << "错误代码: " << WSAGetLastError() << endl;

	//}

	return 0;
}
int Socket::Socket_Recv() {
	char Buffer[MAXBYTES] = { 0 }; // 文件缓冲区
	char wb_files[MAXBYTE] = { 0 };
	FILE* fp = fopen(wb_file, "wb");
	//如果录入文件不存在的话就创建一个新的文件
	if (fp == NULL) {
		fp = fopen(wb_file, "w");
	}
	unsigned long long len_file = 0;
	if (fp == NULL) {
		cout << "操作文件时出错" << endl;
		system("pause");
	}
	else {
		cout << "接收文件时间: ";
		OBTION_TIME();

		unsigned long long g_fileSizes = 0;
		char rev_buffer[MAXBYTES] = { 0 };//接收文件的长度
		int rev_len = recv(clientSock, rev_buffer, MAXBYTE, 0);
		if (rev_len > 0) {
			rev_buffer[rev_len] = '\0';
			for (int i = 0; i < strlen(rev_buffer); i++) {
				g_fileSizes = g_fileSizes * 10 + ((unsigned long long)rev_buffer[i] - 48);
			}
		}
		double start_time = START_TIME();
		memset(&Buffer, 0, MAXBYTES);
		unsigned long long  size = 0;
		//当成功接收文件(size > 0)时,判断写入的时候文件长度是否等于接收的长度
		while ((size = recv(clientSock, Buffer, MAXBYTES, 0)) > 0) {

			if (Buffer[size - 3] == 'e' && Buffer[size - 2] == 'n' && Buffer[size - 1] == 'd')
			{
				char buffer[MAXBYTES] = { 0 };
				for (int i = 0; i < strlen(Buffer) - 3; i++) {
					buffer[i] = Buffer[i];
				}
				len_file += size - 3;
				size -= 3;
				if (fwrite(buffer, sizeof(char), size, fp) < size) {
					cout << "写入出错,部分文件缺失。" << endl;
					break;
				}
				cout.width(3);//i的输出为3位宽
				if ((len_file * 100 / g_fileSizes) % 5 > 0) {
					cout << (len_file * 100 / g_fileSizes) << "%";
					cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
				}
				break;
			}
			else {
				if (fwrite(Buffer, sizeof(char), size, fp) < size) {
					cout << "写入出错,部分文件缺失。" << endl;
					break;
				}
				len_file += size;
			}
			cout.width(3);//i的输出为3位宽
			if ((len_file * 100 / g_fileSizes) % 5 > 0) {
				cout << (len_file * 100 / g_fileSizes) << "%";
				cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
			}
			//清空缓存区以便下一次接收
			memset(&Buffer, 0, MAXBYTE);
		}
		cout << "接收完成" << endl;
		cout << "接受文件大小: ";
		len_file = (unsigned long long)len_file;
		getByteSize(len_file);
		cout << "文件结束接受时间: ";
		OBTION_TIME();
		double end_time = END_TIME();
		double currentTime = 0;
		currentTime = (double)(end_time - start_time) / CLOCKS_PER_SEC;
		cout << "接收文件耗时: " << currentTime << "s" << endl;
		fclose(fp);
	}
	return 0;
}
string Socket::TYPE_file() {
	string end_file = "";
	char Temporary[1024] = { 0 };
	char file[1024] = { 0 };
	int index_last = 0;
	int ret = recv(clientSock, file, 100, 0);
	for (int i = strlen(file) - 1; i >= 0; i--) {
		if (file[i] == '\\') {
			index_last = i;
			break;
		}
	}
	index_last++;
	end_file += "\\";
	if (ret > 0) {
		file[ret] = '\0';
		for (int i = index_last; i < strlen(file); i++) {
			end_file += file[i];
		}
	}
	return end_file;
}
void Socket::REVER_file(string file, string filename) {
	int i = 0;
	memset(wb_file, 0, sizeof(wb_file));
	int len_file = file.length();
	int len_filename = filename.length();
	for (i = 0; i < len_file; i++) {
		wb_file[i] = file[i];
	}
	int j = 0;
	for (i = len_file; i < (len_file + len_filename) && j < len_filename; j++, i++) {
		wb_file[i] = filename[j];
	}
	wb_file[i] = '\0';
}
void Socket::CLEAR() {
	closesocket(clientSock);
	关闭网络库 
	if (WSACleanup() != 0) {
		err("WSACleanup");
		return;
	}
	cout << "客户端连接已关闭。" << endl;
	system("pause");

}

#include"tcpSocket.h"
#define MAXBYTES 1024
int main() {
	Socket* soc = new Socket();
	
	while (true) {
		string ip;
		cout << "请输入目标机器的IP:";
		cin >> ip;
		bool flag = soc->TARGE_FILE(ip);
		if (flag == true)break;
		else {
			cout << "IP地址错误或者目标主机不存在" << endl;
			continue;
		}
	}
	string filename;
	char buf[MAXBYTE] = { 0 };
	int g_fileSize = 0;
	cout << "请输入文件路径: ";
	cin >> filename;
	for (int i = 0; i < filename.length(); i++) {
		buf[i] = filename[i];
	}
	buf[filename.length()] = '\0';
	struct _stat64 st;
	_stat64(buf, &st);
	g_fileSize = st.st_size;
	char* Buffer = new char[g_fileSize + 1];
	FILE* fp = fopen(buf, "rb+");
	if (fp == NULL) {
		cout << "文件打开错误" << endl;
		return 0;
	}
	int size = 0;
	int width = soc->GetWidth(buf);
	int height = soc->GetHeight(buf);
	if ((size=fread(Buffer, sizeof(char), g_fileSize, fp) )> 0) {
		soc->send_image(width, height, 2, Buffer, g_fileSize);
	}
	else {
		cout << "传输出现问题" << endl;
	}
	delete[]Buffer;
	/*
	link nod = soc->initstruct();
	memset(Buffer, 0, sizeof(Buffer));
	memcpy(Buffer, &nod, sizeof(link));
	soc->send_once(Buffer, sizeof(link) + 1);*/
	/*
	while (true) {
		string wb_file;
		char Tempoary[1024] = { 0 };
		char Buffer[MAXBYTES] = { 0 };
		cout << "首先请客户端输入传输文件路径: ";
		string file;
		cin >> file;
		soc->SEND_FILE(file);
		int ret = recv(soc->Getcientsock(), Tempoary, 10, 0);
		if (ret < 0)continue;
		soc->MAIN_SOCKET();
		cout << "其次请客户端输入想要写入的文件(不用输入文件名): ";
		cin >> wb_file;
		string st = "ESC";
		string end_file = soc->TYPE_file();
		send(soc->Getcientsock(), st.c_str(), st.length(), 0);
		soc->REVER_file(wb_file, end_file);
		soc->Socket_Recv();
	}*/
	soc->CLEAR();
	delete  soc;
	return 0;
}

2.服务端:


#pragma once
#ifndef _TCPSERVER_H_
#define _TCPSERVER_H_
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <process.h>
#include <fstream>
#include <string>
#include<time.h>
#define MAX 10*1024
#define err(errMsg) printf("[error] %s failed,code %d\
line:%d\n",errMsg, WSAGetLastError(),__LINE__)

#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件

using namespace std;
class Server {
    private:
        SOCKET clientSock;
        SOCKET servSocket;
        int id;
        char  wb_file[MAXBYTE];//写入文件路径
        char  wb_files[MAXBYTE];//发送文件路径
        char filename[MAXBYTE];
        unsigned long long g_fileSize;
    public:
        //获取clientSock
        SOCKET GetclientSock();
        //获取servSocket
        SOCKET GetservSocket();
        //获取当前日期
        void OBTION_TIME();
        //获取开始时间
        double START_TIME();
        //获取结束时间
        double END_TIME();
        //获取文件大小
        void  getByteSize(unsigned long long size);
        //返回以MB为单位的文件大小
        unsigned long long RETURN_MB(unsigned long long size);
        //绑定和监听
        void TARGE_FILE();
        //返回文件的类型
        string TYPE_file();
        //LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
        static DWORD WINAPI transmmit(const LPVOID arg);
        //系统的实现以上函数
        int MAIN_Server();
        //接收文件
        void REVER_file(string file, string fileanme);
        //清理网络库和关闭
        void CLEAR();
        //发送图片
        static DWORD WINAPI run(const LPVOID arg);
        //发送图片的线程
        void Thread();
        //一次性的接收发送的文件等
        bool recv_once(char *buffer,int length);
        //处理接收到的信息
        void solve_file();
        //根据分类来处理信息,鼠标,键盘,文件,图片等
        void do_handle(char *Buffer, int length);
        //发送文件
        int Server_Send();
        //发送文件1
        void SEND_FILE(string file);
};
//定义结构体用来设置
typedef struct my_file {
    SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信
    sockaddr_in clientAddr; //用于保存客户端的socket地址
    int id; //文件块的序号
}F;
struct Potocol {
    int length;//代表整个数据包的长度
};
struct LogicHeader {
    int type;//代表业务类型
    int logic_raw_size;//裸数据的长度
    int log_raw_offset;//裸数据的偏移
};
struct image {//图像业务
    int width;
    int height;
    int depth;
    char data[0];
};
struct imageSer {
    LogicHeader lh;
    image images;
};
#endif // !_TCPSERVER_H_



#include "tcpServer.h"
#define MAXBYTES 300*1024
mutex m;
//获取当前日期
void Server::OBTION_TIME() {
	SYSTEMTIME start; //windows.h中  
	GetLocalTime(&start);//time.h的tm结构体一样的效果  
	cout << start.wYear << "/" << start.wMonth << "/" << start.wDay << " " << start.wHour << ":" << start.wMinute << ":" << start.wSecond << endl;
}
//获取开始时间
double Server::START_TIME() {
	DWORD start_time;
	start_time = GetTickCount64();
	return (double)start_time;
}
//获取结束时间
double Server::END_TIME() {
	DWORD end_time;
	end_time = GetTickCount64();
	return double(end_time);
}
//获取文件大小
void  Server::getByteSize(unsigned long long size) {
	unsigned long long rest = 0;
	if (size < 1024) {
		cout << size << "B" << endl;
		return;
	}
	else {
		size /= 1024;
	}
	if (size < 1024) {
		cout << size << "KB" << endl;
		return;
	}
	else {
		rest = size % 1024;
		size /= 1024;
	}
	if (size < 1024) {
		size *= 100;
		cout << (size / 100) << "." << (rest * 100 / 1024 % 100) << "MB" << endl;

		return;
	}
	else {
		size = size * 100 / 1024;
		cout << (size / 100) << "." << (size % 100) << "GB" << endl;
		return;
	}
}
string Server::TYPE_file() {
	string end_file = "";
	char Temporary[1024] = { 0 };
	char file[1024] = { 0 };
	int index_last = 0;
	int ret = recv(GetclientSock(), file, 100, 0);
	for (int i = strlen(file) - 1; i >= 0; i--) {
		if (file[i] == '\\') {
			index_last = i;
			break;
		}
	}
	index_last++;
	end_file += "\\";
	if (ret > 0) {
		file[ret] = '\0';
		for (int i = index_last; i < strlen(file); i++) {
			end_file += file[i];
		}
	}
	return end_file;
}
void Server::TARGE_FILE() {
	//加载网络库
	WSADATA wsaData;
	//第一个参数是winsocket load的版本号(2.2)
	if (WSAStartup(MAKEWORD(2, 3), &wsaData) != 0) {
		err("WSAStartup");
		return;
	}
	//创建服务器端的socket(协议族, sokcet类型)
	servSocket = socket(AF_INET, SOCK_STREAM, 0);//如果改成SOCK_DGRAM则使用UDP
	if (servSocket == INVALID_SOCKET) {
		err("SOCKET");
		return;
	}
	sockaddr_in servAddr; //服务器的socket地址,包含sin_addr表示IP地址,sin_port保持端口号和sin_zero填充字节
	memset(&servAddr, 0,  sizeof(SOCKADDR)); //初始化socket地址
	servAddr.sin_family = AF_INET; //设置使用的协议族
	servAddr.sin_port = htons(3725); //设置使用的端口
	servAddr.sin_addr.s_addr = INADDR_ANY; //define s_addr = S_un.S_addr
	 //将之前创建的servSocket和端口,IP地址绑定
	if (bind(servSocket, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		err("bind");
		return;
	}
	HANDLE hThread[2];
	hThread[0]=CreateThread(NULL, 0, &run, this, 0, NULL);
	WaitForMultipleObjects(1, hThread, TRUE, INFINITE);
	/*
	listen(servSocket, 1); //监听服务器端口
	sockaddr_in clntAddr;
	int nSize = sizeof(clntAddr);
	cout << "等待连接..." << endl;
	clientSock = accept(servSocket, (SOCKADDR*)&clntAddr, &nSize);
	if (clientSock == INVALID_SOCKET) {
		err("accept");
	}
	cout << "连接成功" << endl;*/
}
DWORD WINAPI Server::run(const LPVOID arg) {
	Server* ser = (Server*)arg;
	listen(ser->servSocket, 1); //监听服务器端口
	sockaddr_in clntAddr;
	int nSize = sizeof(clntAddr);
	cout << "等待连接..." << endl;
	ser->clientSock = accept(ser->servSocket, (SOCKADDR*)&clntAddr, &nSize);
	if (ser->clientSock == INVALID_SOCKET) {
		err("accept");
	}
	cout << "连接成功" << endl;
	return 0;
}
void Server::REVER_file(string file, string filename) {
	int i = 0;
	int len_file = file.length();
	int len_filename = filename.length();
	for (i = 0; i < len_file; i++) {
		wb_file[i] = file[i];
	}
	int j = 0;
	for (i = len_file; i < (len_file + len_filename) && j < len_filename; j++, i++) {
		wb_file[i] = filename[j];
	}
	wb_file[i] = '\0';
}
SOCKET Server::GetclientSock() {
	return clientSock;
}
SOCKET Server::GetservSocket() {
	return servSocket;
}
/**/
bool Server::recv_once(char *buffer,int length) {
	int byteReceivedOnce = 0;
	int bytesReceivedAll = 0;
	while (bytesReceivedAll <length) {
		if ((byteReceivedOnce = recv(clientSock, buffer + bytesReceivedAll, length - bytesReceivedAll, NULL)) < 0) {
			return false;
		}
		bytesReceivedAll += byteReceivedOnce;
	}
	return true;
}
void Server::do_handle(char *Buffer, int length) {
	LogicHeader& lh = *(LogicHeader*)Buffer;
	
	//测试传输图片是否完整
	FILE* fp = fopen("D:\\3.jpg", "wb");
	if (fp == NULL) {
		fp = fopen("D:\\3.jpg", "w");
	}
	switch (lh.type) {
		case 1:
			imageSer & image = *(imageSer*)Buffer;
			char* raw_data = Buffer + image.lh.log_raw_offset;
			int Logic_lengths = image.lh.logic_raw_size;
			raw_data[Logic_lengths] = '\0';
			
			fwrite(raw_data, sizeof(char), Logic_lengths, fp);
			fclose(fp);
			cout << "发送数据包大小 = " << length << endl;
			cout << "数据类型       = " << lh.type << endl;
			cout << "裸数据长度     = " << Logic_lengths << endl;
			cout << "裸数据的偏移   = " << lh.log_raw_offset << endl;
			cout << "图像宽度       = " << image.images.width << endl;
			cout << "图像高度       = " << image.images.height << endl;
			cout << "图片深度       = " << image.images.depth << endl;
			break;
	}
}
void Server::solve_file() {
	int length = 0;
	int ret = recv(clientSock, (char*)&length, sizeof(length),NULL);
	cout << "ret = " <<ret<< endl;
	cout << "length = " << length << endl;
	if (ret == sizeof(length)) {
		try {
			char* buf = new char[length+100];
			memset(buf, 0, sizeof(buf));
			if (recv_once(buf, length)) {
				do_handle(buf, length);
			}
			delete []buf;
		}catch (exception& e) {
			cout << e.what() << endl;
		}
	}
	
}
void Server::Thread() {
	CreateThread(NULL, 0, &run, this, 0, NULL);
}
void Server::SEND_FILE(string file) {
	int i = 0;
	char Temporary_file[MAXBYTE] = { 0 };//保存发送文件的格式
	memset(wb_file, 0, sizeof(wb_file));
	for (i = 0; i < file.length(); i++) {
		wb_file[i] = file[i];
		Temporary_file[i] = file[i];
	}
	wb_file[i] = '\0';
	Temporary_file[i] = '\0';
	send(clientSock, Temporary_file, strlen(Temporary_file), 0);
	struct _stat64 st;
	_stat64(wb_file, &st);
	g_fileSize = st.st_size;
}
int Server::MAIN_Server() {
	char Buffer[MAXBYTES] = { 0 }; // 文件缓冲区
	char wb_files[MAXBYTE] = { 0 };
	FILE* fp = fopen(wb_file, "wb");
	//如果录入文件不存在的话就创建一个新的文件
	if (fp == NULL) {
		fp = fopen(wb_file, "w");
	}
	unsigned long long len_file = 0;
	if (fp == NULL) {
		cout << "操作文件时出错" << endl;
		system("pause");
	}
	else {
		cout << "接收文件时间: ";
		OBTION_TIME();
		
		unsigned long long g_fileSizes = 0;
		char rev_buffer[MAXBYTES] = { 0 };//接收文件的长度
		int rev_len = recv(clientSock, rev_buffer, MAXBYTE, 0);
		if (rev_len > 0) {
			rev_buffer[rev_len] = '\0';
			for (int i = 0; i < strlen(rev_buffer); i++) {
				g_fileSizes = g_fileSizes * 10 + ((unsigned long long)rev_buffer[i] - 48);
			}
		}
		double start_time = START_TIME();
		memset(&Buffer, 0, MAXBYTES);
		unsigned long long  size = 0;
		//当成功接收文件(size > 0)时,判断写入的时候文件长度是否等于接收的长度
		while ((size = recv(clientSock, Buffer, MAXBYTES, 0)) > 0) {
			if (Buffer[size - 3] == 'e' && Buffer[size - 2] == 'n' && Buffer[size - 1] == 'd')
			{
				char buffer[MAXBYTES] = { 0 };
				for (int i = 0; i < strlen(Buffer) - 3; i++) {
					buffer[i] = Buffer[i];
				}
				len_file += size - 3;
				size -= 3;
				if (fwrite(buffer, sizeof(char), size, fp) < size) {
					cout << "写入出错,部分文件缺失。" << endl;
					break;
				}
				cout.width(3);//i的输出为3位宽
				if ((len_file * 100 / g_fileSizes) % 5 > 0) {
					cout << (len_file * 100 / g_fileSizes) << "%";
					cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
				}
				break;
			}
			else {
				if (fwrite(Buffer, sizeof(char), size, fp) < size) {
					cout << "写入出错,部分文件缺失。" << endl;
					break;
				}
				len_file += size;
			}
			cout.width(3);//i的输出为3位宽
			if ((len_file * 100 / g_fileSizes) % 5 > 0) {
				cout << (len_file * 100 / g_fileSizes) << "%";
				cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
			}
			//清空缓存区以便下一次接收
			memset(&Buffer, 0, MAXBYTE);
		}
		cout << "接收完成" << endl;
		cout << "接受文件大小: ";
		len_file = (unsigned long long)len_file;
		getByteSize(len_file);
		cout << "文件结束接受时间: ";
		OBTION_TIME();
		double end_time = END_TIME();
		double currentTime = 0;
		currentTime = (double)(end_time - start_time) / CLOCKS_PER_SEC;
		cout << "接收文件耗时: " << currentTime << "s" << endl;
		fclose(fp);
	}
	return 0;

}
DWORD WINAPI Server::transmmit(const LPVOID arg) {
	//上锁是为了方便看输出
	m.lock();

	//F* temp = (F*)arg;
	Server* so = (Server*)arg;
	/*
	获取文件的序号int file_id = temp->id;
	获取客户机的端口号ntohs(temp -> clientAddr.sin_port);
	*/
	cout << "测试开始,等待服务端发送消息..." << endl;
	//从客户端处接受数据
	/*
		char Buffer[MAXBYTE] = { 0 }; //缓冲区
		recv(temp->clientSocket, Buffer, MAXBYTE, 0); //recv方法 从客户端通过clientScocket接收
		cout << "线程" << temp->id << "从客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口收到:" << Buffer << endl;
	*/
	char* file_name; //文件路径
	char File_Alias[100] = { 0 };
	file_name = so->wb_file;
	unsigned long long len_file = 0;
	FILE* fp = fopen(file_name, "rb");

	if (fp == NULL) {
		cout << "文件" << file_name << "出错或不存在" << endl;
	}
	else {
		/*
		获取文件大小
		注意这个地方不能使用unsigned long long,因为当文件传输很大的时候,ftell返回的是long
		fseek(fp, 0, SEEK_END);//将读取的文件指针放到文件末尾
		g_fileSize = ftell(fp);
		fseek(fp, 0, SEEK_SET);//指针移到文件开头*/
		
		string send_file_len;
		send_file_len = to_string(so->g_fileSize);
		send(so->clientSock, send_file_len.c_str(), send_file_len.length(), 0);
		cout << "发送文件时间: ";
		so->OBTION_TIME();
		double start_time = so->START_TIME();
		char Buffer[MAXBYTES] = { 0 }; //文件缓冲区
		unsigned long long  size = 0; //读取的文件长度
		
		while ((size = fread(Buffer, sizeof(char), MAXBYTES, fp)) > 0) {
			//返回非0值表示send错误
			if (send(so->clientSock, Buffer, (unsigned long long)size, NULL) < 0){
				cout << "传输出错,请检查网络配置。" << endl;
				break;
			}
			len_file += size;
			cout.width(3);//i的输出为3位宽
			if ((len_file * 100 / so->g_fileSize) % 5 > 0) {
				cout << (len_file * 100 / so->g_fileSize) << "%";
				cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
			}
			size = 0;
			//每次读取完之后清空缓存区,以便下一块文件读入
			memset(&Buffer, 0, MAXBYTES);
		}
		const char* t = "end";
		send(so->clientSock, t, strlen(t), NULL);
		cout << so->id << "线程已成功发送" << file_name << endl;
		cout << "发送文件大小: ";
		so->getByteSize(len_file);
		cout << "文件发送结束时间: ";
		so->OBTION_TIME();
		double end_time = so->END_TIME();
		double currentTime = 0;
		currentTime = (double)(end_time - start_time) / CLOCKS_PER_SEC;
		cout << "发送文件耗时: " << currentTime << "s" << endl;
		fclose(fp);
	}
	/*
		发送简单的字符串到客户端
		const char* s = "Server file";
		send(temp->clientSocket, s, strlen(s)*sizeof(char)+1, NULL);
		cout << "线程" << temp->id << "通过客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口发送:" << s << endl;
	*/

	m.unlock();

	return 0;
}
int Server::Server_Send() {
	//建立连接
	//while (true) {
	cout << "已建立连接。" << endl;
	char Buffer[MAXBYTES] = { 0 }; // 文件缓冲区
	char wb_file[100] = { 0 }; //写入的文件
	//“句柄” 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;
	HANDLE hThread[2];
	for (int i = 0; i < 1; i++) {
		sockaddr_in clntAddr;
		memset(&clntAddr, 0, sizeof(SOCKADDR));
		//使用 API 的 CreateThread, 它执行完入口函数后会自动退出, 无需 ExitThread;
		hThread[i] = CreateThread(NULL, 0, &transmmit, this, 0, NULL);
	}
	//等待子线程完成
	WaitForMultipleObjects(1, hThread, TRUE, INFINITE);
	cout << "错误代码: " << WSAGetLastError() << endl;

	//}

	return 0;
}
void Server::CLEAR() {
	//关闭socket,释放winsock
	if (this != nullptr) {
		closesocket(clientSock);
		closesocket(servSocket);
	}
	关闭网络库 
	if (WSACleanup()!=0) {
		err("WSACleanup");
		return;
	}
	cout << "服务器连接已关闭。" << endl;
	system("pause");
}


#include "tcpServer.h"
#define MAXBYTES 1024
int main() {
	Server* ser = new Server();
	ser->TARGE_FILE();
	ser->solve_file();
	/*
	while (true) {
		char buffer[MAXBYTES] = { 0 };
		string wb_file;
		char Tempoary[MAXBYTES] = { 0 };
		cout << "其次请服务端输入想要写入的文件(不用输入文件名): ";
		cin >> wb_file;
		string st = "ESC";
		string end_file = ser->TYPE_file();
		send(ser->GetclientSock(), st.c_str(), st.length(), 0);
		ser->REVER_file(wb_file, end_file);
		ser->MAIN_Server();
		cout << "首先请服务端输入传输文件路径: ";
		string file;
		cin >> file;
		ser->SEND_FILE(file);
		int ret = recv(ser->GetclientSock(), Tempoary, 10, 0);
		if (ret < 0)continue;
		ser->Server_Send();
	}*/
	ser->CLEAR();
	delete ser;
	return 0;
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验内容 根据自定义的协议规范,使用 Socket 编程接口编写基本的网络应用软件。 掌握 C 语言形式的 Socket 编程接口用法,能够正确发送和接收网络数据包。 开发一个客户端,实现人机交互界面和与服务器的通信。 开发一个服务端,实现并发处理多个客户端的请求。 程序界面不做要求,使用命令行或最简单的窗体即可。 功能要求如下: 运输层协议采用 TCP 客户端采用交互菜单形式,用户可以选择以下功能: a) 连接:请求连接到指定地址和端口的服务端。 b) 断开连接:断开与服务端的连接。 c)获取时间: 请求服务端给出当前时间。 d)获取名字:请求服务端给出其机器的名称。 e)活动连接列表:请求服务端给出当前连接的所有客户端信息(编号、IP 地址、端口等) f)发消息:请求服务端把消息转发给对应编号的客户端,该客户端收到后显示在屏幕上 g) 退出:断开连接并退出客户端程序 3.服务端接收到客户端请求后,根据客户端传过来的指令完成特定任务: a)向客户端传送服务端所在机器的当前时间。 b)向客户端传送服务端所在机器的名称。 c)向客户端传送当前连接的所有客户端信息。 d)将某客户端发送过来的内容转发给指定编号的其他客户端。 e)采用异步多线程编程模式,正确处理多个客户端同时连接,同时发送消息的情况。 根据上述功能要求,设计一个客户端和服务端之间的应用通信协议。 本实验涉及到网络数据包发送部分不能使用任何的 Socket 封装类,只能使用最底层的 C 语言形式的 Socket API。 本实验可组成小组,服务端和客户端可由不同人来完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值