上机考试作弊,也不是不可以呢

对网络编程的第一次探索

请注意,本文编写于 363 天前,最后修改于 363 天前,其中某些信息可能已经过时。

前言

大一的 C++ 课学校新开发了一款 OJ,大概一两周就有一次上机考试,软件的防作弊机制就是每个学号绑定一个IP地址,单学号登录多IP、单IP被多个学号登录都会触发重修小彩蛋。不过,正所谓时势造英雄,当人人都在抱怨的时候为什么不想一个脱离防作弊机制的手段呢?

本文将介绍我那段时间写的一个程序,基于 TCP 的 Winsock 编程实现文件的传送。考试时大家埋头思索,这时候把答案通过IP地址传送给喜欢的妹子,脱单似乎就近在咫尺了啊。

思路

Winsock 的学习看微软官网手册就够了,一步一步跟着敲下来,基本上就算是入门了。

下面简单讲一下 Winsock 编程的基本思路:

Server端:创建嵌套字并初始化,创建socket,初始化socket(将端口号及IP地址绑定),listen监听,accept不断接受连接,接受到连接后,捕获socket,recv获得内容,通信完成后关闭socket。

Client端:创建嵌套字并初始化,创建socket,绑定端口号及目标IP地址,connect连接服务器,send发送消息,通信结束后关闭socket。

完整代码

使用软件:codeblocks

用户端:TransferFile-Client.cpp

// TransferFile-Client.cpp:Define the entry point for the console application.
// Program Function : Transfer file by C/S : Part Client
// Author : zhaoyixiang
// E-mail : 674965440@qq.com
// Date : 2018-5-1

#define _WIN32_WINNT  0x501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <cstring>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 512

using namespace std;

int main(int argc, char const *argv[])
{
    WSADATA wsaData;

    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0)
    {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }


    /*
        Creating a Socket for the Client
        Declare an addrinfo object that contains a sockaddr structure and initialize these values.
        For this application, the Internet address family is unspecified
        so that either an IPv6 or IPv4 address can be returned.
        The application requests the socket type to be a stream socket for the TCP protocol.
    */
    struct addrinfo *result = NULL, *ptr = NULL, hints;
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
    if (iResult != 0)
    {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    

    char temp[DEFAULT_BUFLEN];
    strcpy(temp, "main.cpp");
    // create file
    FILE * fp = fopen(temp, "rb"); // binary mode for read
    if(fp == NULL)
    {
        printf("open file %s failed\n", temp);
        return -1;
    }

    SOCKET ConnectSocket = INVALID_SOCKET;
    ptr = result;
    ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
    if (ConnectSocket == INVALID_SOCKET)
    {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }


    /*
        Connecting to a Socket
        For a client to communicate on a network, it must connect to a server.
    */
    iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    if (iResult == SOCKET_ERROR)
    {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }


    /*
        Sending and Receiving Data on the Client
    */
    int recvbuflen = DEFAULT_BUFLEN;
    char recvbuf[DEFAULT_BUFLEN];
    char sendbuf[DEFAULT_BUFLEN];

    int num = 0;
    while(!feof(fp))
    {
        num = fread(temp, 1, DEFAULT_BUFLEN, fp);
        send(ConnectSocket, temp, num, 0);
    }
    printf("server file transfer success\n");

    fclose(fp);
    

    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR)
    {
        printf("Shutdown failed: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    closesocket(ConnectSocket);


    WSACleanup();
    return 0;
}

服务端:TransferFile-Server.cpp

// TranferFile-Server.cpp:Define the entry point for the console application.
// Program Function : Transfer file by C/S : Part Server
// Author : zhaoyixiang
// E-mail : 674965440@qq.com
// Date : 2018-5-1

#define _WIN32_WINNT  0x501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <cstring>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 512

using namespace std;

int main(int argc, char const *argv[])
{
    WSADATA wsaData;

    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0)
    {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }


    char file_name[DEFAULT_BUFLEN];
    char temp[DEFAULT_BUFLEN];
    strcpy(file_name, "receive test");
    //create file
    FILE *fp = fopen(file_name, "wb");
    if(fp == NULL)
    {
        printf("create file %s failed\n", file_name);
        return -1;
    }


    /*
        Creating a Socket for the Server
    */
    struct addrinfo *result = NULL, hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0)
    {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    SOCKET ListenSocket = INVALID_SOCKET;
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET)
    {
        printf("Error at sock(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }


    /*
        Binding a Socket
        For a server to accept client connections,
        it must be bound to a network address within the system.
    */
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR)
    {
        printf("bind failed: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    freeaddrinfo(result);



    /*
        Listening on a Socket
        After the socket is bound to an IP address and port on the system,
        the server must then listen on that IP address and port
        for incoming connection requests.
    */
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        printf("Listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }


    /*
        Accepting a Connection
        Once the socket is listening for a connection,
        the program must handle connection requests on that socket.
    */
    SOCKET ClientSocket;
    ClientSocket = INVALID_SOCKET;
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET)
    {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }


    /*
        Receiving and Sending Data on the Server
    */
    char recvbuf[DEFAULT_BUFLEN];
    int iSendResult;
    int recvbuflen = DEFAULT_BUFLEN;


    //receive data from server
    int num = 0;
    while (1)
    {
        num = recv(ClientSocket, temp, DEFAULT_BUFLEN, 0);
        if(0 == num)
            break;
        fwrite(temp, 1, num, fp);
    }
    printf("transmission done\n");

    fclose(fp);


    // shutdown the connection for sending since no more data will be sent
    // the client can still use the ConnectSocket for receiving data
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR)
    {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    // cleanup
    closesocket(ClientSocket);



    WSACleanup();

    return 0;
}

注意事项

codeblocks 调用第三方 API 还是比较麻烦的,首先需要在文件头部声明:

#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

还需要在 codeblocks 中配置:

选择project -> Build options

Linker settings -> other linker options -> 输入-lws2_32 -> OK

使用方法

先运行 server 文件,再用命令行运行 client 文件并输入接受方的IP地址,事先准备好的 main.cpp(可以更换名称)即可发送。

Teamwork 时,自己持有 Client 文件,给妹子 Server 文件,然后趁着月黑风高为所欲为。

补充

当然了,真正操作的时候不要傻乎乎的将生成的TransferFile-Client.exe / TransferFile-Server.exe文件直接摆在桌面上,监考老师只要学过英语就能发现你在作弊好吧!!!

到网上搜一个教程把文件图标改了,再把文件名改成常用软件名,例如notepad++,这样老师一般不会注意到。


其实后来我根本没用过这个软件,因为每次考试我都坐在老师身边

添加新评论