IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 计算机网络课程设计(Winsock实现FTP) -> 正文阅读

[C++知识库]计算机网络课程设计(Winsock实现FTP)

老师的要求是:要用Winsock库,实现FTP,要求在自己的电脑上完成客户端和服务端,要有图形化界面。

由于Winsock我也是现学的,所以不一定用的很好,而且c/c++也不太会,所以可能会有很多不完美的地方,仅供大家写课设时参考,最后我会放上我自学的网站。

话不多说,先上源码

服务端

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdlib>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll

using namespace std;

#define BUF_SIZE 1024

boolean exit_flag = true;

int main() {
    printf("服务端已启用!\n");
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0);

    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = PF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(1234);
    bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    listen(servSock, 20);

    SOCKADDR clntAddr;
    int nSize = sizeof(SOCKADDR);

    char ch_flag[20] = {0};//功能选择标记
    
    while (exit_flag) {
        SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
        memset(ch_flag, 0, 20);
        recv(clntSock, ch_flag, 20, 0);
        if (strcmp(ch_flag, "1") == 0) {
            printf("用户选择上传功能!");
            //文件创建
            FILE* fp = fopen("upload.txt", "wb");  //以二进制方式打开(创建)文件
            if (fp == NULL) {
                printf("文件无法创建,请按任意键退出!\n");
                system("pause");
                exit(0);
            }

            //循环接收数据,直到文件传输完毕
            char buffer[BUF_SIZE] = { 0 };  //文件缓冲区
            int nCount;
            while ((nCount = recv(clntSock, buffer, BUF_SIZE, 0)) > 0) {
                fwrite(buffer, nCount, 1, fp);
            }
            fclose(fp);
            if (buffer[0] != NULL) {
                puts("用户上传成功!");
                /*char* flag = "1";
                send(clntSock,flag,sizeof(char)+strlen(flag),0);*/
            }
            else {
                puts("用户上传失败!");
                /*char* flag = "0";
                send(clntSock, flag, sizeof(char) + strlen(flag), 0);*/
            }
        }
        else if (strcmp(ch_flag, "2") == 0) {
            printf("用户选择下载功能!");
            //先检查文件是否存在
            FILE* fp = fopen("a.txt", "rb");  //以二进制方式打开文件
            if (fp == NULL) {
                printf("未找到文件,按任意键退出!\n");
                char* reflBuf = NULL;
                send(clntSock, reflBuf, sizeof(reflBuf), 0);
                system("pause");
                exit(0);
            }

            //循环发送数据,直到文件结尾
            char buffer[BUF_SIZE] = { 0 };  //缓冲区
            int nCount;
            while ((nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0) {
                send(clntSock, buffer, nCount, 0);
            }

            shutdown(clntSock, SD_SEND);  //文件读取完毕,断开输出流,向客户端发送FIN包
            recv(clntSock, buffer, BUF_SIZE, 0);  //阻塞,等待客户端接收完毕

            fclose(fp);
            printf("文件已被下载!\n");
        }
        else if(strcmp(ch_flag, "0") == 0)
        {
            printf("用户选择退出功能!");
            exit_flag = false;
            break;
        }
        closesocket(clntSock);
    }
    
    
    //closesocket(clntSock);
    closesocket(servSock);
    WSACleanup();
    printf("客户端已退出,");
    system("pause");
    return 0;
}

客户端

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdlib>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#include<graphics.h>

using namespace std;

#define BUF_SIZE 1024

void download(SOCKET sock);
void upload(SOCKET sock);
void initDLL();

//static boolean d_flag = false;//下载标识初始化
//static boolean u_flag = false;//上传标识初始化
TCHAR* a;

int main() {
    initgraph(600, 400);//窗口初始化
    setbkmode(TRANSPARENT);//背景透明
    IMAGE bk;
    loadimage(&bk, _T('bk1.jpg'), 887, 1920);
    putimage(0, 0, &bk);
    settextcolor(0xFF0000);//字体深蓝色
    outtextxy(220,80,_T("请 选 择 你 的 操 作 ~"));
    setlinestyle(PS_SOLID, 3);//设置画线样式
    setlinecolor(0xFFFF00);//绿不绿蓝不蓝的按钮颜色
    rectangle(100, 180, 160, 220);
    rectangle(270, 180, 330, 220);
    rectangle(440, 180, 500, 220);
    //自绘制按钮图标
    RECT r1 = { 100,180,160,220 };
    RECT r2 = { 270,180,330,220 };
    RECT r3 = { 440,180,500,220 };
    drawtext(_T("上传"), &r1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    drawtext(_T("下载"), &r2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    drawtext(_T("退出"), &r3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    //鼠标实现点击
    MOUSEMSG m;//鼠标指针

    initDLL();//dll初始化
    while (true) {     
        SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        sockaddr_in sockAddr;
        memset(&sockAddr, 0, sizeof(sockAddr));
        sockAddr.sin_family = PF_INET;
        sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        sockAddr.sin_port = htons(1234);
        //connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
        connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
        m = GetMouseMsg();//获取一条鼠标消息
        if (m.x <= 160 && m.x >= 100 && m.y >= 180 && m.y <= 220) {//上传
            setlinecolor(BLACK);//线条颜色
            rectangle(100, 180, 160, 220);
            if (m.uMsg == WM_LBUTTONDOWN) {
                char* ch = "1";
                send(sock, ch, sizeof(char) + strlen(ch), 0);
                 upload(sock);
                FlushMouseMsgBuffer();//清除鼠标缓冲区信息
            }
        }
        else if (m.x <= 330 && m.x >= 270 && m.y >= 180 && m.y <= 220) {//下载
            setlinecolor(BLACK);//线条颜色
            rectangle(270, 180, 330, 220);
            if (m.uMsg == WM_LBUTTONDOWN) {
                char* ch = "2";
                send(sock, ch, sizeof(char) + strlen(ch), 0);
                //下载文件
                download(sock);
                FlushMouseMsgBuffer();
            }
        }
        else if (m.x <= 500 && m.x >= 440 && m.y >= 180 && m.y <= 220) {//退出
            setlinecolor(BLACK);//线条颜色
            rectangle(440, 180, 500, 220);
            if (m.uMsg == WM_LBUTTONDOWN) {
                char* ch = "0";
                send(sock, ch, sizeof(char) + strlen(ch), 0);
                FlushMouseMsgBuffer();
                break;
            }
        }
        else {
            setlinecolor(0xFFFF00);//绿不绿蓝不蓝的按钮颜色
            rectangle(100, 180, 160, 220);
            rectangle(270, 180, 330, 220);
            rectangle(440, 180, 500, 220);
        }
        closesocket(sock);
        
    }

    WSACleanup();
    //system("pause");
    closegraph();
    
    //文件接收完毕后直接关闭套接字,无需调用shutdown()
    
    
    //system("pause");
    return 0;
}

void initDLL() {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
}

void download(SOCKET sock) {
    //文件创建
    FILE* fp = fopen("download.txt", "wb");  //以二进制方式打开(创建)文件

    if (fp == NULL) {
        //printf("文件无法创建,请按任意键退出!\n");

        return;
        //system("pause");
        //exit(0);
    }

    //循环接收数据,直到文件传输完毕
    char buffer[BUF_SIZE] = { 0 };  //文件缓冲区
    int nCount;
    while ((nCount = recv(sock, buffer, BUF_SIZE, 0)) > 0) {
        fwrite(buffer, nCount, 1, fp);
    }

    fclose(fp);
    //system("pause");
}

void upload(SOCKET sock) {
    //char* filepath = "b.txt";  //文件名
    FILE* fp = fopen("b.txt", "rb");  //以二进制方式打开文件
    if (fp == NULL) {
        //printf("未找到文件,按任意键退出!\n");
        char* sendBuf = NULL;
        send(sock, sendBuf, sizeof(sendBuf), 0);
        return;
        //system("pause");
        //exit(0);
    }
    //循环发送数据,直到文件结尾
    char buffer[BUF_SIZE] = { 0 };  //缓冲区
    int nCount;
    while ((nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0) {
        send(sock, buffer, nCount, 0);
    }
    shutdown(sock, SD_SEND);  //文件读取完毕,断开输出流,向客户端发送FIN包
    recv(sock, buffer, BUF_SIZE, 0);  //阻塞,等待客户端接收完毕

    //u_flag = true;

    fclose(fp);
}

然后,我们来看一下效果图。
客户端
服务端
可以看到,客户端是有图形化界面的,所以得先安装easyx库,安装方法可以直接搜索一下。

因为我用的是VS2019,所以会有部分安全问题。

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

这两条都是处理安全问题的宏。

#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll

这两条分别是导入Winsock2库和需要的资源。
(winsock也有这个库,但是winsock2更全且向下兼容)

WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);

声明出一个资源文件的对象,然后将DLL初始化,启用2.2版本的库

先说服务端

SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);

创建了两个套接字对象,都是服务端本身。
同样是套接字,类型不同是因为带 _in 的可以将ip和端口号分开操作。
memset()是c++的内置函数,用来初始化内存的
地址族变量只在Linux里有区别,所以看到有人用AF_INET和我这里的PF_INET是一样的,端口号的话可以随便设置,只要在可用端口号范围(1024~65535)就行了。

bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
listen(servSock, 20);

bind()就是用来绑定上面两个类型不同的套接字,相当于服务端套接字同时有了名字和详细信息。
listen()是用来监听客户端的,会让服务端进入阻塞状态。

然后,在while循环里定义客户端套接字并连接。把客户端套接字放进循环里是为了出循环后就可以主动关闭套接字,以防它的接收缓冲区影响后续操作。(好奇的同学可以自己放到外面试一下,就会发现fin包接收的极其缓慢,再次进入监听也极其缓慢)

SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);

accept()是当服务端在监听状态下,有客户端接入后建立连接。

char ch_flag[20] = {0};//功能选择标记

这是一个用来监听客户端是按了哪个按钮的变量,获取数据的方式也是通过套接字之间的传输。

//循环接收数据,直到文件传输完毕
char buffer[BUF_SIZE] = { 0 };  //文件缓冲区
int nCount;
while ((nCount = recv(clntSock, buffer, BUF_SIZE, 0)) > 0) {
	fwrite(buffer, nCount, 1, fp);
}

首先定义了一个缓冲区,recv()接收流数据,写入缓冲区,recv()会返回接收的字节数,所以只要没接收完,nCount就大于0,再用IO流写入到本地文件。

send(clntSock, reflBuf, sizeof(reflBuf), 0);

send是用来发送数据的函数。

closesocket(clntSock);
closesocket(servSock);
WSACleanup();

分别关闭客户端套接字,服务端套接字,DLL。

再说客户端

initgraph(600, 400);//窗口初始化
setbkmode(TRANSPARENT);//背景透明
IMAGE bk;
loadimage(&bk, _T('bk1.jpg'), 887, 1920);
putimage(0, 0, &bk);
settextcolor(0xFF0000);//字体深蓝色
outtextxy(220,80,_T("请 选 择 你 的 操 作 ~"));
setlinestyle(PS_SOLID, 3);//设置画线样式
setlinecolor(0xFFFF00);//绿不绿蓝不蓝的按钮颜色
rectangle(100, 180, 160, 220);
rectangle(270, 180, 330, 220);
rectangle(440, 180, 500, 220);
//自绘制按钮图标
RECT r1 = { 100,180,160,220 };
RECT r2 = { 270,180,330,220 };
RECT r3 = { 440,180,500,220 };
drawtext(_T("上传"), &r1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
drawtext(_T("下载"), &r2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
drawtext(_T("退出"), &r3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

用easyx库建立了一个600x400的紫的界面,设置了各种参数,包括按钮的坐标。
下面用while循环实现了各个功能,按钮的点击特效是通过检测鼠标位置不断重绘按钮最外圈线条实现的。

SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);
connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));

上面配置了套接字,connect()用于与服务端连接。(相应的,服务端用accept()连接)
其他的就和服务端一样了。


下面附上我自学的网址:
http://c.biancheng.net/cpp/html/3029.html
希望这篇博客可以帮到你们~

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-01 13:42:06  更:2022-01-01 13:43:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:43:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码