基于TCP聊天文件传输系统(C语言版)

项目简介

本程序的文件传输系统的实现包含服务端模块、客户端模块,使用流式套接字。在设计时设计客户端和服务端两个界面时,客户端的所有信息发往服务器端,再由服务器端进行消息的分析处理并做出相应的操作,服务器端时所有信息的中心。本程序可实现互相聊天、互相传送、以及查询删除文件。
在这里插入图片描述

  • 系统界面
    在这里插入图片描述
    2. 服务器端功能

  • 连接控制:启动服务器,断开服务器、侦听、连接客户端、打印客户端连接信息;

  • 信息处理:接收客户端与服务器交互的信息,并做出相应的处理;

  • 聊天功能:接收客户端聊天请求,进行一对一聊天;

  • 文件传输:接收客户端文件传输请求,可进行发送、接收、查询、删除文件;

3. 客户端功能

  • 连接功能:输入服务器地址,实现连接服务器及断开功能;
  • 信息处理:接收由服务器端发送来的信息,并做出相应的相应;
  • 聊天功能:向服务器端发送聊天请求,等待服务器端回应,进行一对一聊天;
  • 文件传输:向服务器发送文件传输请求,可进行上传、下载、续传、删除文件、查询文件列表文件;

4. 代码段
server.cpp

主界面

#include "stdio.h"
#include "winsock2.h"
#include "stdlib.h"
#include "windows.h"
#include "direct.h"
#include "io.h"
#include <time.h>
#pragma comment(lib,"ws2_32.lib")
#define SERVERIP "127.0.0.1"
#define SERVERPORT 3333
SOCKET sockServer;
SOCKADDR_IN addrServer;    //实例化一个addr来存放地址信息
SOCKADDR_IN peerAddr;
SOCKET sockConn;
#define SIZE 8192 
#define BUFSIZE 8192 
typedef struct LS {
	char name[1024];		//文件名
	int size;			//文件字节数
}LS;
LS listfile[1000];
int num=0;
int filenum=0;
char filename[1024];
int recvFile();
int sendFile(char *filename);
int sendFileList();
int delFileList();//删除文件
int testNum();
int print_clientIP(SOCKET s,sockaddr_in name,int *namelen);
void listFile(char *path, int layer);
void SockStartup();
void recvRequest();
void closeSocket();
void sendWords();
char recvBuf[BUFSIZE];
char sendBuf[BUFSIZE];
int len = sizeof(sockaddr);
int main(void)
{
	system("color e0"); //添加背景色
	printf("\t\t\t☆*****************************☆\n");
	printf("\t\t\t☆  欢迎使用本系统!(服务器端)  ☆\n");
	printf("\t\t\t☆*****************************☆\n");
	
	while(1) {
		SockStartup();
		recvRequest();
		closeSocket();
		WSACleanup();
	}
	system("pause");
	return 0;
}

启动socket,与客户端建立连接

void SockStartup() {
	WSADATA data;
	//初始化win socket网络库,申请2.2的版本
	int ret=WSAStartup(MAKEWORD(2,2),&data);
	if (SOCKET_ERROR==ret)
	{
		return;
	}
	
	//创建一个无指定协议的流式套接字 
	sockServer=socket(AF_INET,SOCK_STREAM,0);
	if (INVALID_SOCKET==sockServer)
	{
		WSACleanup();
		return;
	}
	if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2){
		printf("请求协议版本失败!\n");
		return;
	}
	
	memset(&addrServer,0,sizeof(struct sockaddr_in));
	addrServer.sin_family=AF_INET;                          //指定IP地址地址版本人为IPV4
	addrServer.sin_addr.S_un.S_addr=inet_addr(SERVERIP);    //存放服务器ip地址
	addrServer.sin_port=htons(SERVERPORT);                 //存放端口信息

	ret=bind(sockServer,(struct sockaddr*)&addrServer,sizeof(struct sockaddr));
	if (SOCKET_ERROR==ret)
	{
		closesocket(sockServer);
		WSACleanup();
		return;
	}

	//监听连接请求的状态以及接收客户发出的连接请求,1为正在等待连接的最大队列长度 
	printf("监听客户的连接……\n");
	ret=listen(sockServer,5);
	if (SOCKET_ERROR==ret)
	{
		closesocket(sockServer);
		WSACleanup();
		return;
	}

	int len=sizeof(struct sockaddr);
	
	//accept()接收客户端由connect()发出的连接请求 ,原来的sock 
	sockConn=accept(sockServer,NULL,&len);
	if (INVALID_SOCKET==sockConn)
	{
		printf("错误"); 
		closesocket(sockServer);
		WSACleanup();
		return;
	}
	
	int peerLen=20;
	print_clientIP(sockConn,peerAddr,&peerLen);
	printf("接收到客户端的连接\n");
	printf("..........连接成功!..........\n");
}

关闭socket

void closeSocket() {
	closesocket(sockConn);
}

打印客户端的IP

int print_clientIP(SOCKET s,sockaddr_in name,int *namelen){//打印客户端ip
	getpeername(s, (struct sockaddr *)&name, namelen); //获取connfd表示的连接上的对端地址
       
	printf("接收到客户端%s的连接\n",inet_ntoa(name.sin_addr));

	return 0;
}

请求回应函数

void recvRequest() {
	//char choiceRev[10];
	while(1) {
		Sleep(2000);
		system("cls"); //清屏
		printf("监听客户端的请求......\n");
		if(SOCKET_ERROR==recv(sockConn,recvBuf,sizeof(recvBuf),0) ){
			printf("与客户端断开连接!\n");	
			Sleep(2000);
			system("cls");
			return;
		} else if(!strcmp("1",recvBuf)) {
			printf("客户端请求与你聊天!你是否同意请求?(Y/N)\n");
			fflush(stdin);      //清空缓存区
			gets(sendBuf);
			send(sockConn,sendBuf,sizeof(sendBuf),0);
			//sendto(sockServer,)
			if(strcmp("Y",sendBuf)==0 || strcmp("y",sendBuf)==0){
				sendWords();
				Sleep(2000);
			}else{
				printf("重新监听客户端的请求……\n");
			}
		}else if(!strcmp("2",recvBuf)) {
			printf("客户端请求你发送文件!你是否同意请求?(Y/N)\n");
			fflush(stdin);
			gets(sendBuf);
			send(sockConn,sendBuf,sizeof(sendBuf),0);
			if(strcmp("Y",sendBuf)==0 || strcmp("y",sendBuf)==0){
				printf("请输入要发送文件的文件名:\n ");
				fflush(stdin);
				gets(filename);	
				sendFile(filename);
				Sleep(2000);
			}else{
				printf("重新监听客户端的请求……\n");
			}
		}else if(!strcmp("3",recvBuf)) {
			printf("客户端请求向你发送文件!你是否同意请求?(Y/N)\n");
			fflush(stdin);  //清空缓存区
			gets(sendBuf);
			int a;
			send(sockConn,sendBuf,sizeof(sendBuf,0), 0);
			if(strcmp("Y",sendBuf)==0 || strcmp("y",sendBuf)==0)
			{
				do 
				{
					fflush(stdin);
					a = recvFile();
					Sleep(2000);
					if (a ==-1) //如果文件名输错
					{
						continue;
					}
					recv(sockConn,recvBuf,sizeof(recvBuf),0);
				} while ((strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0));
			}else{
				printf("重新监听客户端的请求……\n");
			}	
		}else if(!strcmp("4",recvBuf)) {
			//path文件名可修改自己电脑里的目录
			char path[255] = "F:\\dirent-master";     
			printf("客户端请求查询目录!你是否同意请求?(Y/N)\n");
			fflush(stdin);  //清空缓存区
			gets(sendBuf);
			send(sockConn,sendBuf,sizeof(sendBuf,0), 0);
			if(strcmp("Y",sendBuf)==0 || strcmp("y",sendBuf)==0)
			{
				num = 0;
				memset(listfile, 0, sizeof(listfile));
				listFile(path,0);
				sendFileList();
				Sleep(2000);
			}
			else{
				printf("重新监听客户端的请求……\n");
			}
		}else if(!strcmp("5",recvBuf)) {//删除文件
			printf("请输入要查询的目录\n");
			char path[255];
			fflush(stdin);  //清空缓存区
			gets(path);
			printf("客户端请求查询目录!你是否同意请求?(Y/N)\n");
			fflush(stdin);  //清空缓存区
			gets(sendBuf);
			send(sockConn,sendBuf,sizeof(sendBuf,0), 0);
			if(strcmp("Y",sendBuf)==0 || strcmp("y",sendBuf)==0)
			{
				num = 0;
				memset(listfile, 0, sizeof(listfile));
				listFile(path,0);
				delFileList();
				Sleep(2000);
			}
			else{
				printf("重新监听客户端的请求……\n");
			}
		}else if(!strcmp("6",recvBuf)) {//猜数字游戏
			printf("客户端请求玩猜数字游戏!你是否同意请求?(Y/N)\n");
			fflush(stdin);      //清空缓存区
			gets(sendBuf);
			send(sockConn,sendBuf,sizeof(sendBuf),0);
			if(strcmp("Y",sendBuf)==0 || strcmp("y",sendBuf)==0){
				testNum();
				Sleep(2000);
			}else{
				printf("重新监听客户端的请求……\n");
			}
		}else if(strcmp("0",recvBuf)==0){
			printf("与客户端断开连接!\n");printf("recvBuf:%s", recvBuf);
			Sleep(2000);
			system("cls"); //清屏
			return;
		} else {
			printf("recvBuf:%s", recvBuf);
		}

	}
}

小游戏

int testNum(){//猜数字游戏
	srand((int)time(0));
	int num=rand()%10;
	int findnum;
	fflush(stdin);      //清空缓存区
	sendBuf[0]=num;
	printf("答案是:%d\n",num);
	//send(sockConn,sendBuf,sizeof(sendBuf),0);
	
	//while(1){
		recv(sockConn,recvBuf,sizeof(recvBuf),0);
		findnum=recvBuf[0]-'0';
		if(num==findnum){
			printf("客户端猜的是%d,对了! 答案是:%d\n",findnum,num);
			sendBuf[1]=1;
			send(sockConn,sendBuf,sizeof(sendBuf),0);
			//break;
		}else if(findnum<num||findnum>num){
			//printf("猜小了\n");
			sendBuf[1]=0;
			printf("客户端猜的是%d,错了! 结果是:%d\n",findnum,num);
			send(sockConn,sendBuf,sizeof(sendBuf),0);
		}
	//}
	return 0;
}

与客户端聊天功能

void sendWords() {
	system("cls");
	printf("开始聊天!输入exit退出聊天程序\n");
	while(1) {
		if(SOCKET_ERROR==recv(sockConn,recvBuf,sizeof(recvBuf),0) ){
			printf("错误!\n");	
			return;
		} 
		if(strcmp("exit",recvBuf)==0) {
			printf("对方已退出聊天程序!\n");
			return;
		}
		printf("client:\n %s\n\n",recvBuf);				
		printf("server:\n ");
		fflush(stdin); //清空缓存区
		gets(sendBuf);
		send(sockConn,sendBuf,sizeof(sendBuf),0);
		if(strcmp("exit",sendBuf)==0)
		{
			system("cls");
			printf("退出聊天程序!\n重新等待客服端的请求\n");
			break;
		}
		printf("\n");
	}
}

服务器发送文件

int sendFile(char *filename) {
	
	int ret,cnt,len1,lenlast;

	FILE *fp;
	int totlen;
	int file_block_length;
	int count=0;            //发送的次数
	int send_sum = 0;       //当前已发送文件长度
	int current = 0;       //进度条长度
	int progress = 50;      //统计接受大小
	
	
	fp=fopen(filename,"rb");
	
	if (NULL==fp)
	{
		printf("~~~~~~~ 文件打开失败 ! ~~~~~~~\n");
		strcpy(sendBuf,"ERROR");
		send(sockConn,sendBuf,strlen(sendBuf)+1,0);
		return -1;
	}
	
	//设置流 stream 的文件位置为给定的偏移 offset ,相当于定位到文件尾 
    fseek(fp,0,SEEK_END);
    
    //函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。相当得到文件大小 */ 
	totlen=ftell(fp);

	printf("文件%s的长度为%d字节\n",filename,totlen);

	memset(sendBuf,0,sizeof(sendBuf));
	strcpy(sendBuf,filename);
	
	//向客户端发送文件名 
	ret=send(sockConn,sendBuf,strlen(sendBuf)+1,0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	memset(recvBuf,0,sizeof(recvBuf));
	
	//收到客户端发回来的文件名确定信息 
	ret=recv(sockConn,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret|| (strcmp(sendBuf,recvBuf)!=0) )
	{
		printf("未收到文件名确认信息");
		return -1;
	} 

	printf("向客户端发送文件名成功\n");

	memset(sendBuf,0,sizeof(sendBuf));
	
	//将文件大小长度 转换成字符串存入sendBuf 指向的内存空间  
	itoa(totlen,sendBuf,10);
	
	//给客户端发送文件长度 
	ret=send(sockConn,sendBuf,strlen(sendBuf)+1,0);
	
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	memset(recvBuf,0,sizeof(recvBuf));
	ret=recv(sockConn,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret|| (strcmp(sendBuf,recvBuf)!=0) )
	{
		return -1;
	} 

	printf("向客户端发送文件长度成功\n");
	
	
	cnt=totlen/SIZE;
	len1=SIZE;
	if (totlen%SIZE)
	{
		lenlast=totlen-cnt*SIZE;
		cnt=cnt+1;
	}

	printf("\n\n文件长度为%d字节,\n每次传送%d字节,\n需要分%d次传送\n",
		totlen,SIZE,cnt);
		
	//重新指向文件头 
	rewind(fp);
	
	//读取文件指定大小元素,且元素个数 
	Sleep(1000);
	while(!feof(fp)) {
		//file_block_length = fread(sendBuf,SIZE,1,fp);
		file_block_length = fread(sendBuf, sizeof(char), SIZE, fp);
		send_sum += file_block_length;
		current = send_sum / (totlen / progress);
		printf("\r");
		printf("[");
		for (int i = 0; i < progress; i++) {
			putchar(i < current ? '=' : '+');
		}
		printf("]");
		printf(" %8ld/%ld %6.2f%%", send_sum, totlen, (float)send_sum / totlen * 100);

		if(file_block_length < SIZE) {
			int rest_length = totlen-count*SIZE;
			//printf("\n剩余字符%d\n", rest_length);

			if(send(sockConn,sendBuf,rest_length,0) < 0) {
				printf("####L(%d) send file:%s failed\n", __LINE__, filename);
				break;
			}
		} else if(send(sockConn,sendBuf,SIZE,0) < 0) {
			printf("####L(%d) send file:%s failed\n", __LINE__, filename);
			break;
		}
		memset(sendBuf,0,sizeof(sendBuf));
		count++;
	} 
	printf("\n这个程序传送了%d次\n\n", count);
	printf("..........发送成功..........\n");
	fclose(fp);
	return 0;
}

服务器接收文件

int recvFile()
{
	int ret;
	FILE *fp;
	long file_size = 0;     //文件大小 
	int recv_sum = 0;       //当前接收文件长度
	int current = 0;       //进度条长度
	int progress = 50;      //统计接受大小
	char filename[512];		//文件名

	memset(recvBuf,0,sizeof(recvBuf));
	
	//recv()接收数据,0表示正常接收数据 ,第三个是缓冲区长度 
	ret=recv(sockConn,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	} else if(strcmp(recvBuf,"ERROR") == 0) {
		printf("请重新选择文件\n");
		return -1;
	}

	printf("成功接收客户端发来的文件名: %s\n",recvBuf);

	memset(sendBuf,0,sizeof(sendBuf));
	strcpy(sendBuf,recvBuf);
	
	//send()发送数据 0表示正常发送数据 ,第三个是即将发送的缓冲区字符数 
	ret=send(sockConn,sendBuf,strlen(sendBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	printf("请输入你希望保存的文件名(带扩展名)\n");
	scanf("%s",filename);
	while(_access(".//filename",0) != -1){
		puts("文件已存在!是否覆盖Y/y N/n\n");
		int c = getchar();
		if(getchar() == 'Y' || 'y')
			break;
		else {
			printf("请重新输入你希望保存的文件名(带扩展名)\n");
			scanf("%s",filename);
		}
	}

	fp=fopen(filename,"wb");
  
	if (NULL==fp)
	{
		return -1;
	}

	memset(recvBuf,0,sizeof(recvBuf));
	//再接收文件的长度信息 
	ret=recv(sockConn,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	printf("成功接收客户端发来的文件长度: %s\n",recvBuf);

	file_size = strtol(recvBuf, NULL, 10);       //文件长度赋给一个长整型
	printf("文件长度 :%ld\n", file_size);
	memset(sendBuf,0,sizeof(sendBuf));
	strcpy(sendBuf,recvBuf);

	ret=send(sockConn,sendBuf,strlen(sendBuf),0);
	if (SOCKET_ERROR==ret){
		return -1;
	}

	memset(recvBuf,0,sizeof(recvBuf));

	while((ret = recv(sockConn,recvBuf,SIZE,0))) {
		if(ret<0) {
			printf("####L(%d) current length < 0 !\n", __LINE__);
			fclose(fp);
			exit(1);
		}
		int write_ret = fwrite(recvBuf, sizeof(char), ret, fp);
		//写文件
		if(write_ret < ret) {
			printf("####L(%d) file:%s write failed write_length %d <length %d!\n", __LINE__, filename, write_ret, ret);
			fclose(fp);
			exit(1);
		}
		memset(recvBuf,0,sizeof(recvBuf));
		recv_sum += ret;
		current = recv_sum / (file_size / progress);
		printf("\r");
		putchar('[');
		for (int i = 0; i < progress; i++) {
			putchar(i < current ? '>' : ' '); // 输出> 或者 ' '		
		}
		putchar(']');
		printf(" %8ld/%ld %6.2f%%", recv_sum, file_size, (float)recv_sum / file_size * 100);

		if(recv_sum == file_size) {
			//接收完成
			printf("\n####L(%d) recv file finished ...####\n", __LINE__);
			break;
		} else if(recv_sum > file_size) {
			printf("\n####L(%d) recv file err recv_sum[%ld] ...####\n", __LINE__, recv_sum);
			break;
		}
		Sleep(10);
	}
	
	fclose(fp);
	return 0;	
}

服务器删除文件

int delFileList() {//删除文件
	char filename[1024];

	//printf("文件有%d个:\n",num);
	//printf("\n——列出来的:——\n");
	
	printf("\n");
	int needSend;
	char *buffer;
	int ret;


	fflush(stdin);  //清空缓存区
	//传文件名称列表
	for(int j=0; j<num; j++) {
		needSend = sizeof(listfile[j]);
		buffer=(char*)malloc(needSend);

		memcpy(buffer,&listfile[j],needSend);

		//printf("buffer=%s\n",buffer);
	
		ret=send(sockConn,buffer,sizeof(LS),0);			//用needSend和sizeof(buffer)有问题
		//ret=send(sock,buffer,needSend,0);

		if (SOCKET_ERROR==ret)
		{
			printf("error");
			return -1;
		}
		memset(buffer,0,sizeof(buffer));
		Sleep(100);
		
	}
	Sleep(1000);
	strcpy(buffer,"OK");
	ret=send(sockConn,buffer,sizeof(buffer),0);	
	printf("\n加载完毕!\n");
	Sleep(500);

	//接收文件名
	ret=recv(sockConn,filename,sizeof(filename),0);
	if (SOCKET_ERROR==ret)
	{
		printf("未接收到文件\n");
		return -1;
	} else if (ret == -1 || strcmp(filename,"NOT") == 0) {
		recvRequest();
	} else {
		printf("\n成功接收到文件名\n");
		printf("filename:%s", filename);
		if(!remove(filename)){
			printf("\n删除文件成功\n");
			strcpy(buffer, "OK");
			send(sockConn,buffer,sizeof(buffer),0);	
		}
	}
	return 0;
}

查询文件列表

void listFile(char *path, int layer) {
	
	struct _finddata_t filefind;       //_finddata_t结构用来记录查找到的文件的信息。
	char curr[1024] = {};				//当前路径
	sprintf(curr, "%s\\*.*",path);
	int done=0,i,handle;
	if(0 == strlen(curr))
    {
        printf("文件名不能为空\n");
        return;
	} else {
		 // 查找目录中的第一个文件
		if((handle=_findfirst(curr,&filefind))==-1) {
			printf("未能找到第一个文件\n");
			return;
		}
		//若查找成功,返回0,失败返回-1
		while(!(done=_findnext(handle,&filefind))) {
			if(!strcmp(filefind.name,"..")) continue;      //若为二级目录,则跳过
			for(i=0; i<layer; i++) printf("  ");
			//是否为子目录子目录
			if((_A_SUBDIR == filefind.attrib)) {
				printf("%s(dir):\n",filefind.name);
				sprintf(curr, "%s\\%s",path,filefind.name);	   //在目录后面加上"\\"和搜索到的目录名进行下一次搜索
				listFile(curr, layer+1);
			} else {
				printf("%s\t\t\t%d\n",filefind.name,filefind.size);
				strncpy(listfile[num].name,curr,strlen(curr)-3);
				strcat(listfile[num].name,filefind.name);

				//printf("%s\n", listfile[num].name);
				listfile[num].size = filefind.size;
				num++;				//文件个数
			}

		}
	}
	_findclose(handle);    // 关闭搜索句柄
	
}

向客户端发送查询到的文件列表

int sendFileList() {
	char filename[1024];

	//printf("文件有%d个:\n",num);
	//printf("\n——列出来的:——\n");
	/*
	for(int j=0; j<num; j++) {
		printf("%s\t\t\t\t\t\t%d\n", listfile[j].name, listfile[j].size);
	}
	*/
	printf("\n");
	int needSend;
	char *buffer;
	int ret;

	/*
	char sendNum[10] = {0};
	itoa(num,sendNum,10);
	printf("sendNum;%s\n", sendNum);
	//给客户端发送文件个数
	ret=send(sockConn,sendNum,strlen(sendNum),0);
	if (SOCKET_ERROR==ret) return -1;
	*/

	fflush(stdin);  //清空缓存区
	//传文件名称列表
	for(int j=0; j<num; j++) {
		needSend = sizeof(listfile[j]);
		buffer=(char*)malloc(needSend);

		memcpy(buffer,&listfile[j],needSend);

		//printf("buffer=%s\n",buffer);
	
		ret=send(sockConn,buffer,sizeof(LS),0);			//用needSend和sizeof(buffer)有问题
		//ret=send(sock,buffer,needSend,0);

		if (SOCKET_ERROR==ret)
		{
			printf("error");
			return -1;
		}
		memset(buffer,0,sizeof(buffer));
		Sleep(100);
		
	}
	Sleep(1000);
	strcpy(buffer,"OK");
	ret=send(sockConn,buffer,sizeof(buffer),0);	
	printf("\n加载完毕!\n");
	Sleep(500);

	//接收文件名
	ret=recv(sockConn,filename,sizeof(filename),0);
	if (SOCKET_ERROR==ret)
	{
		printf("未接收到文件\n");
		return -1;
	} else if (ret == -1 || strcmp(filename,"NOT") == 0) {
		recvRequest();
	} else {
		printf("\n成功接收到文件名\n");
		printf("filename:%s", filename);
		sendFile(filename);
	}

	return 0;
}

client.cpp

#include "stdio.h"
#include "stdlib.h"
#include "winsock2.h"
#include "windows.h"
#pragma comment(lib,"ws2_32.lib")
#define SERVERIP "192.168.1.32"
#define SERVERPORT 3333
#define SIZE 8192 //4096//2//
#define BUFSIZE 8192 
int sendFile();
int recvFile();
int checkList();
int delfile();//删除文件
int testNum();//猜数字
void inquire();
void SockStartup();
void closeSocket();
void sendRequest();
void recvWords();
int rightIP(char ip_address[15]);
SOCKET sockClient;
SOCKADDR_IN addrServer;
typedef struct LS {
	char name[1024];		//文件名
	int size;			//文件字节数
}LS;
LS listfile[1000];
char recvBuf[BUFSIZE];
char sendBuf[BUFSIZE];
int main(void)
{
	system("color e0");
	printf("\t\t\t☆***************************☆\n");
	printf("\t\t\t☆  欢迎使用本系统!(客户端)  ☆\n");
	printf("\t\t\t☆***************************☆\n");

	WSACleanup();
	SockStartup();
	while(1) {
		sendRequest();
		if(sockClient == NULL) {
			WSACleanup();
			SockStartup();
		}
	}

	closeSocket();
	WSACleanup();

	system("pause");
	return 0;
}
//建立socket连接
void SockStartup() {
	WSADATA data;
	
	int ret=WSAStartup(MAKEWORD(2,2),&data);
	if (SOCKET_ERROR==ret){
		return;
	}

	sockClient=socket(AF_INET,SOCK_STREAM,0);
	if (INVALID_SOCKET==sockClient)
	{
		perror("create socket fail\n");
		WSACleanup();
		return;
	}
	if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2){
		printf("请求协议版本失败!\n");
		return;
	}

	//memset(&addrServer,0,sizeof(struct sockaddr_in));
	char str[15];
	printf("请输入主机的IP地址——格式如(127.0.0.1)\n");
	gets(str);
	if (rightIP(str) == -1)
	{
		printf("连接不成功!请重新建立连接!\n");
		closesocket(sockClient);
		WSACleanup();
		SockStartup();
		return;
	}
	addrServer.sin_family=AF_INET;
	addrServer.sin_addr.S_un.S_addr=inet_addr(str);
	addrServer.sin_port=htons(SERVERPORT);

	printf("等待服务器......\n");
	//connect()提出与一个人服务器建立连接的请求 
	ret=connect(sockClient,(sockaddr*)&addrServer,sizeof(struct sockaddr));
	if (SOCKET_ERROR==ret)
	{
		printf("连接不成功!请重新建立连接!\n");
		closesocket(sockClient);
		WSACleanup();
		SockStartup();
		return;
	}

	printf("连接服务器成功\n");
}
//关闭socket
void closeSocket(){
	closesocket(sockClient);
}
//请求服务列表
void sendRequest() {
	Sleep(1000);  //睡眠等待2秒
	system("cls");
	printf("★***********************★\n");
	printf("☆     1.聊天            ☆\n");
	printf("☆     2.下载文件        ☆\n");
	printf("☆     3.传送文件        ☆\n");
	printf("☆     4.查看/修改文件   ☆\n");
	printf("☆     5.删除服务端文件  ☆\n");
	printf("☆     6.猜数字游戏      ☆\n");
	printf("☆     0.退出            ☆\n");
	printf("★***********************★\n");
	printf("请选择:\n");
	fflush(stdin);  //清空缓存区
	gets(sendBuf);
	send(sockClient,sendBuf,sizeof(sendBuf),0);
	while(1) {
		if(strcmp("1", sendBuf) == 0) {
			Sleep(2000);
			system("cls");  //清屏
			printf("等待服务器的响应......\n");
			recv(sockClient,recvBuf,sizeof(recvBuf),0);
			if(strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0)
			{
				recvWords();
				return;
			}else{
				printf("recvBuf:%s", recvBuf);
				printf("服务器没有接受你的本次请求!\n");
				printf("请重新发送请求\n");
				return;
			}
		} else if (strcmp("2", sendBuf)  == 0) {
			Sleep(2000);
			system("cls");
			printf("等待服务器的响应......\n");
			recv(sockClient,recvBuf,sizeof(recvBuf),0);
			if(strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0)
			{
				recvFile();
				return;
			} else{
				printf("recvBuf:%s", recvBuf);
				printf("服务器没有接受你的本次请求!\n");
				printf("请重新发送请求\n");
				return;
			}
		}else if (strcmp("3", sendBuf)  == 0) {
			Sleep(2000);
			system("cls");
			printf("等待服务器的响应......\n");
			recv(sockClient,recvBuf,sizeof(recvBuf),0);
			if(strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0)
			{
				int a = sendFile();
				while (a == 0) {
					fflush(stdin);
					printf("是否还要传输文件(Y/N)?\n");
					gets(sendBuf);
					send(sockClient,sendBuf,sizeof(sendBuf),0);
					if (strcmp("Y",sendBuf)==0||strcmp("y",sendBuf)==0)
					{
						a = sendFile();
					} 
					else if(strcmp("N",sendBuf)==0||strcmp("n",sendBuf)==0)
					{
						return;
					}
					else
					{
						printf("输入有误,请重新选择\n");
						break;
					}
				}
			} else{
				printf("recvBuf:%s", recvBuf);
				printf("服务器没有接受你的本次请求!\n");
				printf("请重新发送请求\n");
				return;
			}
		}else if (strcmp("4", sendBuf) == 0) {
			Sleep(2000);
			system("cls");
			printf("等待服务器的响应......\n");
			recv(sockClient,recvBuf,sizeof(recvBuf),0);
			if(strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0)
			{
				fflush(stdin);  //清空缓存区
				checkList();
				return;
			} else{
				printf("recvBuf:%s", recvBuf);
				printf("服务器没有接受你的本次请求!\n");
				printf("请重新发送请求\n");
				return;
			}
		}else if (strcmp("5", sendBuf) == 0) {//删除文件
			Sleep(2000);
			system("cls");
			printf("等待服务器的响应......\n");
			recv(sockClient,recvBuf,sizeof(recvBuf),0);
			if(strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0)
			{
				fflush(stdin);  //清空缓存区
				delfile();
				return;
			} else{
				printf("recvBuf:%s", recvBuf);
				printf("服务器没有接受你的本次请求!\n");
				printf("请重新发送请求\n");
				return;
			}
		}else if(strcmp("6", sendBuf) == 0) {//猜数字游戏
			Sleep(2000);
			system("cls");  //清屏
			printf("等待服务器的响应......\n");
			recv(sockClient,recvBuf,sizeof(recvBuf),0);
			if(strcmp("Y",recvBuf)==0 || strcmp("y",recvBuf)==0)
			{
				//recvWords();
				testNum();
				return;
			}else{
				printf("recvBuf:%s", recvBuf);
				printf("服务器没有接受你的本次请求!\n");
				printf("请重新发送请求\n");
				return;
			}
		}else if (strcmp("0", sendBuf)  == 0) {
			exit(0);
		} else{
			printf("请选择正确的编号\n");
			break;
		}
	}

}
//检测输入的ip合法性
int rightIP(char ip_address[15])
{
	int i,j,k=0;
	int intaddress[4];  //变成整型的ip地址
	char charaddress[3];  //原ip地址
	for (i=0;i<=3;i++)
	{
		for (j=0;j<=3;j++)
		{
			if (ip_address[k] == '.' || ip_address[k] == '\0')
			{
				k++;
				intaddress[i]=atoi(charaddress);
				break;
			}
			charaddress[j]=ip_address[k++];
		}
		charaddress[0]=NULL;
		charaddress[1]=NULL;
		charaddress[2]=NULL;
	}
	i=0;
	while (i<4)
	{
		if (intaddress[i]>255)
		{
			printf("输入非法ip,请重新输入\n");
			return -1;
		}
		i++;
	}
	if (intaddress[0]!=127||intaddress[1]!=0||intaddress[2]!=0||intaddress[3]!=1)
	{
		printf("对不起,能力有限~只能输入127.0.0.1\n");
		return -1;
	}
}
//猜数字小游戏
int testNum(){
	printf("输入你要猜的数字!\n");
	int num,answer;
	fflush(stdin);
	gets(sendBuf);
	send(sockClient,sendBuf,sizeof(sendBuf),0);
	recv(sockClient,recvBuf,sizeof(sendBuf),0);
	num=recvBuf[0];
	answer=recvBuf[1];
	if(answer==1){
		printf("猜对了!\n");
	}else{
		printf("猜错了!,答案是%d\n",num);
	}
	Sleep(2000);
	return 0;
}
//聊天功能
void recvWords(){
	fflush(stdin);  //清空缓存区
	printf("开始聊天!输入exit退出!\n");	
	while(1) {
		printf("client:\n");		
		fflush(stdin);
		gets(sendBuf);
		send(sockClient,sendBuf,sizeof(sendBuf),0);
		if(!strcmp("exit",sendBuf)){
			printf("退出聊天程序!\n");
			break;
		}
		recv(sockClient,recvBuf,sizeof(sendBuf),0);
		if(strcmp("exit",recvBuf)==0)
		{
			Sleep(2000);
			printf("服务器已退出聊天程序!\n");
			printf("重新发送请求\n");
			break;
		}	
		printf("\nserver:\n%s\n\n",recvBuf);
	}
}
//询问是否返回主菜单
void inquire() {
	printf("\n-----是否返回主菜单,Y/y N/n----- \n");
	getchar();
	switch(getchar()){
		case 'Y':
		case 'y':
				sendRequest(); break;
		case 'N':
		case 'n':
				exit(0);break;
		default:
				sendRequest(); break;
	}
}
//接收文件
int recvFile(){
	int ret;
	FILE *fp;
	long file_size = 0;     //文件大小 
	int recv_sum = 0;       //当前接收文件长度
	int current = 0;       //进度条长度
	int progress = 50;      //统计接受大小
	char sendBuf[BUFSIZE],recvBuf[BUFSIZE],filename[512];

	
	//接收文件确认信息
	memset(recvBuf,0,sizeof(recvBuf));
	ret=recv(sockClient,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	} else if(strcmp(recvBuf,"ERROR") == 0) {
		printf("未接收到文件\n");
		return -1;
	}

	printf("成功接收服务端端发来的文件名: %s\n",recvBuf);

	//再次发送确认
	memset(sendBuf,0,sizeof(sendBuf));
	strcpy(sendBuf,recvBuf);
	
	//send()发送数据 0表示正常发送数据 ,第三个是即将发送的缓冲区字符数 
	ret=send(sockClient,sendBuf,strlen(sendBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	//保存文件
	printf("请输入你希望保存的文件名(带扩展名)\n");
	scanf("%s",filename);
	fp=fopen(filename,"wb");
	if (NULL==fp)
	{
		return -3;
	}

	memset(recvBuf,0,sizeof(recvBuf));
	//再接收文件的长度信息 
	ret=recv(sockClient,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -4;
	}
	
	printf("成功接收客户端发来的文件长度: %s\n",recvBuf);

	file_size = strtol(recvBuf, NULL, 10);       //文件长度赋给一个长整型
	printf("文件长度 :%ld\n", file_size);
	memset(sendBuf,0,sizeof(sendBuf));
	strcpy(sendBuf,recvBuf);

	ret=send(sockClient,sendBuf,strlen(sendBuf),0);
	if (SOCKET_ERROR==ret)
	{
		return -5;
	}
	memset(recvBuf,0,sizeof(recvBuf));

	while((ret = recv(sockClient,recvBuf,SIZE,0))) {	
		if(ret<0) {
			printf("####L(%d) current length < 0 !\n", __LINE__);
			fclose(fp);
			exit(1);
		}
		int write_ret = fwrite(recvBuf, sizeof(char), ret, fp);

		//写文件
		if(write_ret < ret) {
			printf("####L(%d) file:%s write failed write_length %d <length %d!\n", __LINE__, filename, write_ret, ret);
			fclose(fp);
			exit(1);
		}

		memset(recvBuf,0,sizeof(recvBuf));
		recv_sum += ret;
		//printf("\nrecv_sum[%ld] ...####\n",recv_sum);
		current = recv_sum / (file_size / progress);
		printf("\r");
		putchar('[');
		for (int i = 0; i < progress; i++) {
			putchar(i < current ? '>' : ' '); // 输出> 或者 ' '
		
		}
		putchar(']');
		printf(" %8ld/%ld %6.2f%%", recv_sum, file_size, (float)recv_sum / file_size * 100);
		if(recv_sum == file_size) {
			//接收完成
			printf("\n####L(%d) recv file finished ...####\n", __LINE__);
			break;
		} else if(recv_sum > file_size) {
			printf("\n####L(%d) recv file err recv_sum[%ld] ...####\n", __LINE__, recv_sum);
			break;
		}
		Sleep(10);
	}

	fclose(fp);
	Sleep(2000);  //睡眠等待2秒
	inquire();
	return 0;
}
//删除文件
int delfile(){
	
	char filename[1024];
	int filenum=0,i=0, ret;
	memset(recvBuf,0,sizeof(recvBuf));
	printf(" filename\n");
	while(recv(sockClient,recvBuf,sizeof(recvBuf),0) != -1) {	
		if(strcmp(recvBuf, "OK") == 0) break;
		memcpy(&listfile[i],recvBuf,sizeof(LS));
		printf("序号[%d] %s\n",i+1, listfile[i].name);
		memset(recvBuf,0,sizeof(recvBuf));
		i++;
		Sleep(0.01);
	}
	filenum=i;
	printf("\n文件有%d个:\n",filenum);
	
	char c;
	printf("\n是否需要删除某个文件 Y/N y/n:");
	c = getchar();
	if(c == 'Y' || c == 'y') {
		printf("\n请选择你要删除的文件序号:\n");
		scanf("%d", &i);
		strcpy(filename,listfile[i-1].name);
		printf("选择的文件名为:%s\n", filename);

		memset(sendBuf,0,sizeof(sendBuf));
		strcpy(sendBuf,filename);
		Sleep(100);
		ret=send(sockClient,sendBuf,strlen(sendBuf)+1,0);
		if (SOCKET_ERROR==ret)
		{
			printf("发送文件名失败!");
			return -1;
		}
		printf("发送文件名成功!\n");
		recv(sockClient,recvBuf,sizeof(recvBuf),0);
		if(!strcmp(recvBuf, "OK")){
			printf("删除文件成功!\n");
		}
		Sleep(1000);

	} else {
		strcpy(sendBuf, "NOT");
		send(sockClient,sendBuf,strlen(sendBuf)+1,0);
		Sleep(2000);
		inquire();
	}
	
	return 0;
}
//查看服务器端文件列表
int checkList(){
	
	char filename[1024];
	int filenum=0,i=0, ret;
	memset(recvBuf,0,sizeof(recvBuf));
	printf(" filename\n");
	while(recv(sockClient,recvBuf,sizeof(recvBuf),0) != -1) {	
		if(strcmp(recvBuf, "OK") == 0) break;
		memcpy(&listfile[i],recvBuf,sizeof(LS));
		printf("序号[%d] %s\n",i+1, listfile[i].name);
		memset(recvBuf,0,sizeof(recvBuf));
		i++;
		Sleep(0.01);
	}
	filenum=i;
	printf("\n文件有%d个:\n",filenum);
	
	char c;
	printf("\n是否需要下载 Y/N y/n:");
	c = getchar();
	if(c == 'Y' || c == 'y') {
		printf("\n请选择你要下载的文件序号:\n");
		scanf("%d", &i);
		strcpy(filename,listfile[i-1].name);
		printf("选择的文件名为:%s\n", filename);

		memset(sendBuf,0,sizeof(sendBuf));
		strcpy(sendBuf,filename);
		Sleep(100);
		ret=send(sockClient,sendBuf,strlen(sendBuf)+1,0);
		if (SOCKET_ERROR==ret)
		{
			printf("发送文件名失败!");
			return -1;
		}
		printf("发送文件名成功!\n");
		recvFile();
	} else {
		strcpy(sendBuf, "NOT");
		send(sockClient,sendBuf,strlen(sendBuf)+1,0);
		Sleep(2000);
		inquire();
	}
	
	return 0;
}
//发送文件
int sendFile(){
	char filename[512];
	int ret,cnt,len1,lenlast;

	printf("\n请输入你需要传送的文件名,带扩展名\n");
	scanf("%s",filename);

	FILE *fp;
	int totlen;
	int file_block_length;
	int count=0;            //发送的次数
	int send_sum = 0;       //当前已发送文件长度
	int current = 0;       //进度条长度
	int progress = 50;      //统计接受大小

	fp=fopen(filename,"rb");
	
	while(fp==NULL) {
		printf("open file error\n");
		printf("\n请输入你需要传送的文件名,带扩展名\n");
		scanf("%s",filename);
		fp=fopen(filename,"rb");
	}
	memset(sendBuf,0,sizeof(sendBuf));
	
	//设置流 stream 的文件位置为给定的偏移 offset ,相当于定位到文件尾 
    fseek(fp,0,SEEK_END);
    
    //函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。相当得到文件大小 */ 
	totlen=ftell(fp);

	printf("文件%s的长度为%d字节\n",filename,totlen);

	
	strcpy(sendBuf,filename);
	
	//向服务器发送文件名 
	ret=send(sockClient,sendBuf,strlen(sendBuf)+1,0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	memset(recvBuf,0,sizeof(recvBuf));
	
	//收到服务器发回来的文件名确定信息 
	ret=recv(sockClient,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret|| (strcmp(sendBuf,recvBuf)!=0) )
	{
		return -1;
	} 

	printf("向服务器发送文件名成功\n");

	memset(sendBuf,0,sizeof(sendBuf));
	
	//将文件大小长度 转换成字符串存入sendBuf 指向的内存空间  
	itoa(totlen,sendBuf,10);
	
	//给服务器发送文件长度 
	ret=send(sockClient,sendBuf,strlen(sendBuf)+1,0);
	if (SOCKET_ERROR==ret)
	{
		return -1;
	}

	memset(recvBuf,0,sizeof(recvBuf));
	ret=recv(sockClient,recvBuf,sizeof(recvBuf),0);
	if (SOCKET_ERROR==ret|| (strcmp(sendBuf,recvBuf)!=0) )
	{
		return -1;
	} 

	printf("向服务器发送文件长度成功\n");
	
	
	cnt=totlen/SIZE;
	len1=SIZE;
	if (totlen%SIZE)
	{
		lenlast=totlen-cnt*SIZE;
		cnt=cnt+1;
	}

	printf("\n\n文件长度为%d字节,\n每次传送%d字节,\n需要分%d次传送\n",
		totlen,SIZE,cnt);
		
	//重新指向文件头 
	rewind(fp);
	
	//读取文件指定大小元素,且元素个数 
	Sleep(1000);
	while(!feof(fp)) {
		//file_block_length = fread(sendBuf,SIZE,1,fp);
		file_block_length = fread(sendBuf, sizeof(char), SIZE, fp);
		send_sum += file_block_length;                 //累计接收文件大小
		current = send_sum / (totlen / progress);     //计算当前进度
		printf("\r");                                 //光标回车
		printf("[");
		for (int i = 0; i < progress; i++) {
			putchar(i < current ? '=' : '+');
		}
		printf("]");
		
		printf(" %8ldKB/%ldKB %6.2f%%", send_sum, totlen, (float)send_sum / totlen * 100);
		if(file_block_length < SIZE) {
			int rest_length = totlen-count*SIZE;
			//printf("\n剩余字符%d\n", rest_length);

			if(send(sockClient,sendBuf,rest_length,0) < 0) {
				printf("####L(%d) send file:%s failed\n", __LINE__, filename);
				break;
			}
		} else if(send(sockClient,sendBuf,SIZE,0) < 0) {
			printf("####L(%d) send file:%s failed\n", __LINE__, filename);
			break;
		}
		memset(sendBuf,0,sizeof(sendBuf));
		count++;
	} 
	printf("\n这个程序传送了%d次\n\n", count);

	fclose(fp);
	Sleep(2000);
	//inquire();
	return 0;

}

这个程序做下来还是有些小bug,但是如果只是传输文件的话,已经是够了的

  • 10
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值