使用easyx实现可视化界面
部分截图:
初始界面:
点击连接服务器后界面:
输入IP和端口号后:
选择上传操作后:
选择下载操作后:
协议设计:
下载文件过程:
客户端发送:download:filename
服务端响应:
如果文件存在,发送文件大小size
如果文件不存在,发送“error”五个字节信息,断开连接
客户端发送:
如果缓冲区开辟完成,发送“ok”两个字节信息
如果缓冲区开辟失败,发送“error”五个字节信息,断开连接
服务端响应:
发送二进制文件信息
上传文件过程:
客户端发送:upload:filename:file_size
服务端响应:
如果缓冲区开辟完成,发送“ok”两个字节信息。
如果缓冲区开辟失败,发送“error”五个字节信息,断开连接
客户端发送:
发送二进制文件信息
代码部分
客户端部分:
/*************************************************************************************
@Description : 客户端
@Version : 1.0
@Author : Nash_Leo
@Date : 2021-12-08 18:46:39
@LastEditors : Nash_Leo
@LastEditTime : 2021-12-08 18:56:41
@FilePath : \\G++\\网络编程\\Client.cpp
@Copyright (C) 2021 Nash_Leo. All rights reserved.
*************************************************************************************/
#include <iostream>
#include <cstdlib>
#include<winsock.h>
#include <string>
#include <fstream>
#include <cstdio>
#include <graphics.h>
#include <conio.h>
#pragma warning( disable : 4996)
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int r[4][4] = { {30,20,130,60},{170,20,220,60},{260,20,310,60},{350,20,400,60} };//三个按钮的二维数组
int button_judge(int x, int y)
{
if (x > r[0][0] && x<r[0][2] && y>r[0][1] && y < r[0][3])return 1;
if (x > r[1][0] && x<r[1][2] && y>r[1][1] && y < r[1][3])return 2;
if (x > r[2][0] && x<r[2][2] && y>r[2][1] && y < r[2][3])return 3;
if (x > r[3][0] && x<r[3][2] && y>r[3][1] && y < r[3][3])return 4;
return 0;
}
void ErrorHandling(const char *s)
{
outtextxy(35, 300, s);
exit(1);
}
void InitiallizationWSA()
{
WSADATA wsaData;
//初始化套接字库
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("初始化套接字库失败!");
//检查版本号
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wHighVersion) != 2)
ErrorHandling("套接字库版本号不符!");
}
void init_figure()
{
int i;
setrop2(R2_COPYPEN);//当前颜色
setlinecolor(BLACK);
setlinestyle(PS_SOLID);//实线
rectangle(30, 100, 420, 330);//外框线
setlinestyle(PS_DOT);//点线
}
void tuxing()
{
//初始化套接字库
InitiallizationWSA();
char server_ip[30];
char port_c[30];
//发送、接收数据的大小
int send_len = 0, recv_len = 0;
int len = 0;
//发送、接收缓冲区
char send_buf[100];
char recv_buf[100];
string send_info;
memset(recv_buf, '\0', 100);
memset(send_buf, '\0', 100);
//定义服务端套接字
SOCKET s_server;
//定义服务端地址
SOCKADDR_IN server_addr;
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
string file_name;
char file_[100];
int port = 0;
string s;
int i, event = 0;
short win_width, win_height;//定义窗口的宽度和高度
win_width = 480; win_height = 360;
initgraph(win_width, win_height);//初始化窗口(黑屏)
for (i = 0; i < 256; i += 5)
{
setbkcolor(RGB(i, i, i));//设置背景色,原来默认黑色
cleardevice();//清屏(取决于背景色)
Sleep(15);//延时15ms
}
init_figure();
RECT R1 = { r[0][0],r[0][1],r[0][2],r[0][3] };
RECT R2 = { r[1][0],r[1][1],r[1][2],r[1][3] };
RECT R3 = { r[2][0],r[2][1],r[2][2],r[2][3] };
RECT R4 = { r[3][0],r[3][1],r[3][2],r[3][3] };
LOGFONT f;
gettextstyle(&f); //获取字体样式
_tcscpy(f.lfFaceName, _T("宋体")); //设置字体为宋体
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
settextcolor(BLACK); //BLACK在graphic.h头文件里面被定义为黑色的颜色常量
drawtext("连接服务器", &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示
drawtext("上传", &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
drawtext("下载", &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
drawtext("退出", &R4, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
setlinecolor(BLACK);
rectangle(r[0][0], r[0][1], r[0][2], r[0][3]);
rectangle(r[1][0], r[1][1], r[1][2], r[1][3]);
rectangle(r[2][0], r[2][1], r[2][2], r[2][3]);
rectangle(r[3][0], r[3][1], r[3][2], r[3][3]);
outtextxy(35, 105, "欢迎使用:文件传输TCP实现——客户端");
MOUSEMSG m;//鼠标指针
while (true)
{
m = GetMouseMsg();//获取一条鼠标消息
switch (m.uMsg)
{
case WM_MOUSEMOVE:
setrop2(R2_XORPEN);
setlinecolor(LIGHTCYAN);//线条颜色为亮青色
setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅
setfillcolor(WHITE);//填充颜色为白色
if (button_judge(m.x, m.y) != 0)
{
if (event != button_judge(m.x, m.y))
{
event = button_judge(m.x, m.y);//记录这一次触发的按钮
fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//有框填充矩形(X1,Y1,X2,Y2)
}
}
else
{
if (event != 0)//上次触发的按钮未被修正为原来的颜色
{
fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//两次同或为原来颜色
event = 0;
}
}
break;
case WM_LBUTTONDOWN:
setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)
for (i = 0; i <= 10; i++)
{
setlinecolor(RGB(25 * i, 25 * i, 25 * i));//设置圆颜色
circle(m.x, m.y, 2 * i);
Sleep(30);//停顿30ms
circle(m.x, m.y, 2 * i);//抹去刚刚画的圆
}
switch (button_judge(m.x, m.y))
{
case 1: {
clearrectangle(31, 101, 419, 329);
char cc[30];
InputBox(server_ip, 30, "请输入服务端IP地址(点分十进制):");
InputBox(port_c, 30, "请输入服务端端口号:");
s = server_ip;
int k = 1;
for (int i = strlen(port_c) - 1; i >= 0; i--)
{
port += int(port_c[i] - '0') * k;
k *= 10;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr(s.c_str());
server_addr.sin_port = htons(port);//5010
if (connect(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
ErrorHandling("服务端连接失败!");
outtextxy(35, 105, "连接服务端成功!");
FlushMouseMsgBuffer();//单击事件后清空鼠标消息
break;
}
case 2: {
clearrectangle(31, 101, 419, 329);
InputBox(file_, 100, "请输入上传文件名称:");
file_name = file_;
//获取文件长度,并发送给服务端
ifstream infile;
infile.open(file_name, ios::in | ios::binary);
if (!infile.good())
//文件不存在,提示
ErrorHandling("文件不存在!");
else
{
infile.seekg(0, ios::end);
int length = infile.tellg();
infile.seekg(0, ios::beg);
send_info = "upload:" + file_name + ":" + to_string(length);
//等待服务端开辟缓冲区
send_len = send(s_server, send_info.c_str(), 100, 0);
if (send_len < 0)
{
ErrorHandling("发送失败!断开连接");
}
memset(recv_buf, '\0', 100);
recv_len = recv(s_server, recv_buf, 100, 0);
if (recv_len < 0)
{
ErrorHandling("接收失败!断开连接");
}
else
{
outtextxy(35, 105, "接收服务端信息:");
cout << "接收服务端信息:" << recv_buf << endl;
outtextxy(160, 105, recv_buf);
}
string recv_info(recv_buf);
if (recv_info == "ok")
{
//服务端开辟缓冲区成功,上传文件数据
//发送整个文件
char* data;
data = new char[length];
infile.read(data, length);
send_len = send(s_server, data, length, 0);
if (send_len < length)
ErrorHandling("发送文件失败!断开连接");
delete data;
outtextxy(35, 125, "文件上传成功");
}
else
outtextxy(35, 125, "服务端开辟缓冲区失败!");
infile.close();
outtextxy(35, 145, "断开连接");
//关闭套接字
closesocket(s_server);
}
FlushMouseMsgBuffer();//单击事件后清空鼠标消息
break;
}
case 3: {
clearrectangle(31, 101, 419, 329);
InputBox(file_, 100, "请输入下载文件名称:");
file_name = file_;
memset(send_buf, '\0', sizeof(send_buf));
string sss = "download:";
for (int i = 0; i < sss.size(); i++)
{
send_buf[i] = sss[i];
}
for (int i = sss.size(); i < sss.size() + file_name.size(); i++)
{
send_buf[i] = file_name[i - sss.size()];
}
send_len = send(s_server, send_buf, 100, 0);
if (send_len < 0)
{
ErrorHandling("发送失败!断开连接");
}
//获取反馈的命令
memset(recv_buf, '\0', 100);
recv_len = recv(s_server, recv_buf, 100, 0);
if (recv_len < 0)
{
ErrorHandling("接收失败!断开连接");
}
else
{
outtextxy(35, 105, "接收服务端信息:");
cout << "接收服务端信息:" << recv_buf << endl;
outtextxy(160, 105, recv_buf);
}
string recv_info(recv_buf);
if (recv_info != "error")
{
//接收文件大小,开辟文件缓冲区
ofstream outfile;
outfile.open(file_name, ios::out | ios::binary | ios::trunc);
if (!outfile.good())
{
//文件无法打开
cerr << "文件无法建立" << endl;
}
else
{
//开辟缓冲区
auto size_of_file = atoi(recv_info.c_str());
char* data = nullptr;
try
{
data = new char[size_of_file];
}
catch (const std::exception&)
{
//失败,发送error
memset(send_buf, '\0', 100);
strcpy(send_buf, "error");
send_len = send(s_server, send_buf, 100, 0);
if (send_len < 0)
{
ErrorHandling("发送失败!断开连接");
}
ErrorHandling("开辟缓冲区失败!");
}
//成功,发送ok
memset(send_buf, '\0', 100);
strcpy(send_buf, "ok");
send_len = send(s_server, send_buf, 100, 0);
if (send_len < 0)
{
ErrorHandling("发送失败!断开连接");
}
recv_len = recv(s_server, data, size_of_file, 0);
outfile.write(data, size_of_file);
outfile.close();
//cout << data << endl;
char l[100]; //存放大小
outtextxy(35, 125, "接收文件大小为:");
sprintf(l, "%d", recv_len);
outtextxy(160, 125, l);
if (recv_len < size_of_file)
{
outtextxy(35, 145, "接收失败!");
//接收的信息不全,需要删除文件
remove(file_name.c_str());
}
else
outtextxy(35, 145, "接收成功!");
outtextxy(35, 165, "断开连接!");
delete data;
}
}
FlushMouseMsgBuffer();//单击事件后清空鼠标消息
//关闭套接字
closesocket(s_server);
break;
}
case 4:
closegraph();
return;
default:
FlushMouseMsgBuffer();//单击事件后清空鼠标消息
break;
}
}
break;
}
system("pause");//暂停,为了显示
}
int main()
{
tuxing();
//释放DLL资源
WSACleanup();
return 0;
}
服务器端部分:
/*************************************************************************************
@Description : 服务器端
@Version : 1.0
@Author : Nash_Leo
@Date : 2021-12-08 18:46:48
@LastEditors : Nash_Leo
@LastEditTime : 2021-12-09 14:41:18
@FilePath : \\G++\\网络编程\\Server.cpp
@Copyright (C) 2021 Nash_Leo. All rights reserved.
*************************************************************************************/
#include <iostream>
#include <fstream>
#include <cstdlib>
#include<winsock.h>
#include <string>
#include "time.h"
#include<windows.h>
#include <exception>
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <windows.h>
//使用localtime
#pragma warning(disable:4996)
//使用套接字库
#pragma comment(lib,"ws2_32.lib")
using namespace std;
string timetoStr() {
char tmp[64];
time_t t = time(NULL);
tm* _tm = localtime(&t);
int year = _tm->tm_year + 1900;
int month = _tm->tm_mon + 1;
int date = _tm->tm_mday;
int hh = _tm->tm_hour;
int mm = _tm->tm_min;
int ss = _tm->tm_sec;
sprintf(tmp, "%04d%02d%02d %02d:%02d:%02d ", year, month, date, hh, mm, ss);
return string(tmp);
}
string GetLogNameByDate() {
char tmp[30];
time_t t = time(NULL);
tm* _tm = localtime(&t);
int year = _tm->tm_year + 1900;
int month = _tm->tm_mon + 1;
int date = _tm->tm_mday;
sprintf(tmp, "%04d%02d%02d.txt", year, month, date);
return string(tmp);
}
void ErrorHandling(const string& s)
{
cerr << timetoStr() << s << endl;
exit(1);
}
//客户端下载文件处理函数
void ClientDownloadFile(SOCKET s_accept, string file_name, string clinet_IP, ostream& out_stream)
{
int send_len = 0, recv_len = 0;
//发送、接收缓冲区
char send_buf[100], recv_buf[100];
memset(send_buf, '\0', 100);
memset(recv_buf, '\0', 100);
string send_info;
ifstream infile;
infile.open(file_name, ios::in | ios::binary);
if (!infile.good())
{
//文件不存在,回复客户端
send_len = send(s_accept, "error", 100, 0);
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送给客户端的信息:" << "error" << endl;
if (send_len < 0)
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 请求文件不存在而断开连接" << endl;
closesocket(s_accept);
return;
}
//文件存在,返回客户端文件大小
else
{
infile.seekg(0, ios::end);
int length = infile.tellg();
infile.seekg(0, ios::beg);
send_info = to_string(length);
send_len = send(s_accept, send_info.c_str(), send_info.length(), 0);
//Sleep(1000);
if (send_len < send_info.length())
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
else
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端信息:" << send_info << endl;
//等待客户端开辟缓冲区,接收信息后再发送文件
memset(recv_buf, '\0', 100);
recv_len = recv(s_accept, recv_buf, 100, 0);
if (recv_len < 0)
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 接收数据失败!断开连接" << endl;
closesocket(s_accept);
return;
}
else
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 接收来自客户端的信息:" << recv_buf << endl;
if (string(recv_buf) == "ok")
{
char* data;
data = new char[length];
infile.read(data, length);
send_len = send(s_accept, data, length, 0);
if (send_len < length)
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端文件信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
delete data;
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 文件发送成功!断开连接" << endl;
closesocket(s_accept);
return;
}
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 客户端缓冲区开辟失败!断开连接" << endl;
closesocket(s_accept);
}
}
//客户端上传文件处理函数
void ClientUploadFile(SOCKET s_accept, string file_name, int file_size, string clinet_IP, ostream& out_stream)
{
int send_len = 0, recv_len = 0;
//发送、接收缓冲区
char send_buf[100], recv_buf[100];
memset(send_buf, '\0', sizeof(send_buf));
memset(recv_buf, '\0', sizeof(recv_buf));
string send_info;
//根据文件大小,开辟文件缓冲区
ofstream outfile;
outfile.open(file_name, ios::out | ios::binary | ios::trunc);
if (!outfile.good())
{
//文件无法打开
send_len = send(s_accept, "error", 100, 0);
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送给客户端的信息:" << "error" << endl;
if (send_len < 0)
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 上传的文件无法建立而断开连接" << endl;
closesocket(s_accept);
return;
}
//文件打开成功,开辟缓冲区,并返回客户端结果
else
{
char* data = nullptr;
try
{
data = new char[file_size];
}
catch (const std::exception&)
{
//失败,发送error
memset(send_buf, '\0', sizeof(send_buf));
strcpy(send_buf, "error");
send_len = send(s_accept, send_buf, 100, 0);
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送给客户端的信息:" << send_buf << endl;
if (send_len < 0)
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 服务端开辟缓冲区失败!断开连接" << endl;
closesocket(s_accept);
return;
}
//成功,发送ok
memset(send_buf, '\0', sizeof(send_buf));
strcpy(send_buf, "ok");
send_len = send(s_accept, send_buf, 100, 0);
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送给客户端的信息:" << send_buf << endl;
if (send_len < 0)
{
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 发送客户端信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
recv_len = recv(s_accept, data, file_size, 0);
outfile.write(data, file_size);
outfile.close();
if (recv_len < file_size)
{
//接收的信息不全,需要删除文件
remove(file_name.c_str());
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 接收客户端文件信息失败!断开连接" << endl;
closesocket(s_accept);
return;
}
else
out_stream << timetoStr() << "客户端为:" << clinet_IP << " 接收客户端文件成功!断开连接" << endl;
delete data;
closesocket(s_accept);
}
}
void InitiallizationWSA()
{
WSADATA wsaData;
//初始化套接字库
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("初始化套接字库失败!");
//检查版本号
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wHighVersion) != 2)
ErrorHandling("套接字库版本号不符!");
}
int main()
{
//创建日志文件,存储日志
string log_file_name = GetLogNameByDate();
ofstream Log;
try
{
Log.open(log_file_name, std::ofstream::out | std::ofstream::app);
cout << "创建日志文件成功!" << endl;
}
catch (ios_base::failure& e)
{
cout << "创建日志文件失败!退出程序" << endl;
exit(1);
}
Log << timetoStr() << "启动:文件传输TCP实现——服务端。。。" << endl;
//初始化套接字库
InitiallizationWSA();
//发送、接收数据的大小
int send_len = 0, recv_len = 0;
int len = 0;
//发送、接收缓冲区
char send_buf[100], recv_buf[100];
string na;
memset(send_buf, '\0', sizeof(send_buf));
memset(recv_buf, '\0', sizeof(recv_buf));
string send_info;
//定义服务端套接字,接受请求套接字
SOCKET s_server, s_accept;
//客户端IP
string clinet_IP;
//定义服务端地址,客户端地址
SOCKADDR_IN server_addr, clnt_addr;
//创建服务端套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(5010);
//绑定套接字
if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
ErrorHandling("套接字绑定失败!");
//监听套接字
if (listen(s_server, SOMAXCONN) < 0)
ErrorHandling("监听套接字失败!");
//接受连接
while (true)
{
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR*)&clnt_addr, &len);
if (s_accept == SOCKET_ERROR)
ErrorHandling("连接失败!");
//获取客户端IP
clinet_IP = inet_ntoa(clnt_addr.sin_addr);
Log << timetoStr() << "连接" << clinet_IP << "成功!" << endl;
//获取客户端发送的文件基本信息:命令:文件名:文件大小
memset(recv_buf, '\0', 100);
recv_len = recv(s_accept, recv_buf, 100, 0);
if (recv_len < 0)
{
Log << timetoStr() << "客户端为:" << clinet_IP << " 接收数据失败!断开连接" << endl;
closesocket(s_accept);
continue;
}
else
{
Log << timetoStr() << "客户端为:" << clinet_IP << " 接收来自客户端的信息:" << recv_buf << endl;
}
//根据协议解析命令数据,格式为命令:文件名:文件大小
string order;//文件操作命令
string file_name;//文件名
int size_of_file;//文件大小
string file_info(recv_buf);
auto id_addr1 = file_info.find_first_of(':');
order = file_info.substr(0, id_addr1);
auto id_addr2 = file_info.find_first_of(':', id_addr1 + 1);
file_name = file_info.substr(id_addr1 + 1, id_addr2 - id_addr1 - 1);
auto Size = file_info.substr(id_addr2 + 1, file_info.length() - id_addr2 - 1);
//下载文件
if (order == "download")
{
ClientDownloadFile(s_accept, file_name, clinet_IP, Log);
}
if (order == "upload")
{
ClientUploadFile(s_accept, file_name, atoi(Size.c_str()), clinet_IP, Log);
}
}
Log.close();
//关闭套接字
closesocket(s_server);
//释放DLL资源
WSACleanup();
//closegraph();//关闭绘图界面
return 0;
}
参考文档:
https://blog.csdn.net/weixin_42119432/article/details/109544038
https://blog.csdn.net/weixin_44044411/article/details/104276757