利用流式套接字传输数据文件

服务器端:
1>建立一个基于对话框的应用程序StreamSocketServer
2>设计界面,将“确定”按钮的标题改为“启动”
3>双击“启动按钮”,添加以下代码,初始化网络,启动服务器监听

void CStreamSocketServerDlg::OnOK() 
{
    // TODO: Add extra validation here
    WSADATA wsaData;
    /*
    typedef struct WSAData
    {
        WORD wVersion;  //为希望使用的Winsock版本
        WORD wHeighVersion;  //返回现有的Winsock库的最高版本
        char szDescription[WSADESCRIPTION_LEN+1];  //通常不用
        char szSystemStatus[WSASYS_STATUE_LEN+1];  //通常不用
        unsigned short iMaxSockets;  //可同时打开的套接字数
        unsigend short iMaxUdpDg;    //数据报最大长度
        char FAR * lpVendorInfo;   //为制定Winsock实施方案的厂商信息所预留,在任何一个Win32平

台上都没有使用这个字段]
    }
    */

    //初始化TCP协议
    BOOL ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
    /*
    int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
    wVersionRequested 用户制定准备加载的Winsock库的版本.
    宏定义MAKEWOARD(X, Y) 获得wVersionRequested的正确值, [X为高位字节,Y为低位字节, 
    lpWSAData 为指向LPWSDATA结构的指针,包含了加载库版本有关的信息
    */
    if(ret != 0)
    {
        MessageBox("初始化网络协议失败!");
        return;
    }

    //创建服务器端套接字
    ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    /*
    SOCKET socket(int af, int type, int protocol);
    af用于制定网络类型,一般取AF_INET,表示该套接字在Internet域中进行通信
    type用于指定套接字类型,SOCK_STREAM表示流套接字, SOCK_DGRAM表示数据报套接字
    protocol用于指定网络协议,默认为0,表示TCP/IP协议
    */
    if(ServerSock == INVALID_SOCKET)
    {
        MessageBox("创建失败!");
        closesocket(ServerSock);   
    }

    //绑定到本地一个端口上
    sockaddr_in localaddr;
    /*
    struct sockaddr_in
    {
        short sin_family;   //必须为AF_INET,表示该socket处于Internet域
        u_short sin_port;   //用于指定服务端口
        struct in_addr sin_addr;  //把一个IP地址保存为一个4字节的数,其数据类型为无符号长整数

类型
        char sin_zero[8];    //只充当填充项角色,使得与sockaddr长度保持一致
    }
    */
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons(6000);  
    /*
    htons的功能:将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian)
    参数u_short hostshort:16位无符号整数
    返回值:TCP/IP网络字节顺序.
    */
    localaddr.sin_addr.s_addr = 0;
    if(bind(ServerSock, (struct sockaddr *)&localaddr, sizeof(sockaddr)) == SOCKET_ERROR)
    {
        MessageBox("绑定地址失败!");
        closesocket(ServerSock);
        WSACleanup();
        return;
    }
    /*
    int bind(SOCKET s, const struct sockaddr FAR *name, int namelen);
    s标识一个未捆绑套接字的句柄,用来等待客户机的连接
    name是赋予套接字的地址
    */

    //将ServerSock设置为异步非阻塞模式,并为它注册各种网络异步事件 
    //m_hWnd为应用程序的主对话框或主窗口的句柄
    if(WSAAsyncSelect(ServerSock, m_hWnd, NETWORK_EVENT, FD_ACCEPT|FD_CLOSE|FD_READ|FD_WRITE) == 

SOCKET_ERROR)
    {
        MessageBox("注册失败!");
        WSACleanup();
        return;
    }
    /*
    int WSAAsyncSelect(
        SOCKET s,    
        HWND hWnd,   //接收消息的窗口句柄
        unsigned int wMsg;  //网络事件发生时要接收的消息
        long lEvent;   //被注册的网络事件
    );
    lEvent参数值及意义:
    FD_READ  希望在套接字s收到数据时收到消息
    FD_WRITE 希望在套接字s可以发送数据时收到消息
    FD_ACCEPT 希望在套接字s收到连接请求是收到消息
    FD_CONNECT  希望在套接字s连接成功是收到消息
    FD_CLOSE 希望在套接字s连接关闭是收到消息
    FD_OOB 希望在套接字s收到外带数据时收到消息

    NETWORK_EVENT 为自定义消息,并添加其响应处理函数OnNetEvent(WPARAM wParam, LPRAM lParam)
     */

    listen(ServerSock, 31); //设置为侦听模式,最多要31个连接
    MessageBox("服务启动成功!");
    CDialog::OnOK();
}

4>注册自定义消息NETWORK_EVENT, 并添加其相应处理函数OnNetEvent
利用流式套接字传输数据文件_第1张图片
在StreamSocketServerDlg.h中添加成员函数:

void OnNetEvent(WPARAM wParam, LPARAM lParam);

在StreamSocketServerDlg.cpp中实现:

void CStreamSocketServerDlg::OnNetEvent(WPARAM wParam, LPARAM lParam)
{
    int iEvent = WSAGETSELECTEVENT(lParam); //调用Winsock API函数,得到网络事件类型
    SOCKET CurSock = (SOCKET)wParam;  //调用Winsock API函数,得到此事件的客户端套接字
    switch(iEvent)
    {
    case FD_ACCEPT:  //客户端连接请求事件
        TRACE("Get client call!\n");
        OnAccept(CurSock); 
        break;
    case FD_CLOSE:   //客户端断开事件
        OnClose(CurSock);
        break;
    case FD_READ:   //客户端网络包到达事件
        OnReceive(CurSock);
        break;
    case FD_WRITE:  //发送网络数据事件
        break;
    default: break;
    }
}

5>添加客户端的连接请求处理函数

void CStreamSocketServerDlg::OnAccept(SOCKET CurSock)
{
    SOCKET TempSock;
    struct socaaddr_in ConSocketAddr;
    int addrlen;
    addrlen = sizeof(ConSocketAddr);
    TempSock = accept(CurSock, (struct sockaddr*)&ConSocketAddr, &addrlen);
    /*
    用于接受连接请求
    SCOKET accept(
        SCOKET s,   //Socket的识别码
        struct sockaddr FAR * addr,  //存放连接的客户端地址
        int FAR* addrlen  //地址长度
    )
    */
    if(ConnectSock == INVALID_SOCKET)
        MessageBox("INVALID_SOCKET");
    TRACE("建立新连接,sock:%d\n", TempSock);  //输出调试信息
    inet_ntoa(ConSocketAddr.sin_addr);
    ConnectSock = TempSock;
    char *filename = "c:\\001.doc";
    if(!SendFile(filename, ConnectSock))
        MessageBox("发送失败!");

}

6>添加发送数据文件函数

BOOL CStreamSocketServerDlg::SendFile(char *name, SOCKET conn)
{
    char *FileName = name;
    SOCKET TcpConn = conn;
    CFile file;
    if(!file.Open(FileName, CFile::modeRead))
    {
        printf("打开%s失败!\n", FileName);
        return FALSE;
    }
    int NumBytes;  //用来保存每次传送时数据块的大小
    UINT Total = 0;  //用来保存套接字已经传送的总的字节数
    int BufSize = 1024;  //发送缓冲区的大小
    int Size = BufSize;  //读取文件的大小
    LPBYTE pBuf = new BYTE[BufSize];  //发送缓冲区
    DWORD dwTemp = 0;
    UINT FileLength = file.GetLength();  //得到文件的大小并发送出去
    send(TcpConn, (char *)&FileLength, 4, 0);
    /*
    int send(  //返回发送的字符总数
        SOCKET s,
        const char FAR *buf,  //存放要传送的资料的暂存区
        int len,  //buf的长度
        int flags   //此函数被调用的方式
    )
    */
    file.SeekToBegin();
    while(Total < FileLength)
    {
        if((int)(FileLength - Total) < Size)
            Size = FileLength - Total;
        Size = file.Read(pBuf, Size);
        /*
        virtual UINT Read(
            void* lpBuf,  //是把资源读入哪里
            UINT nCount   //是读入的字节数
        );
        */
        NumBytes = send(TcpConn, (char *)pBuf, Size, 0);
        if(NumBytes == SOCKET_ERROR)
        {
            send(TcpConn, "ERROR", sizeof("ERROR")+1, 0);
            return FALSE;
        }
        Total += NumBytes;
        file.Seek(Total, CFile::begin);
    }
    delete[] pBuf;
    file.Close();
    closesocket(TcpConn);
    return TRUE;
}

客户端:
1>新建一个基于对话框的程序StreamSocketClient
2>设计界面,添加编辑框m_CtrlPAddress输入IP地址,m_iPort用于输入端口,将“确定”按钮改为“连接”,“取消”改为“关闭”
3>双击“连接”,添加以下代码:

void CStreamSocketClientDlg::OnOK() 
{
    // TODO: Add extra validation here
    UpdateData(FALSE);  //初始化对话框中的数据
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD(2, 2);  //连接两个给定的无符号参数
    err = WSAStartUp(wVersionReqested, &wsaData);
    if(err != 0)
        return;
    if(LOBYTE(wsaData.wVersion) != 1) || HIBYTE(wsaData.wVersion) != 1) //LOBYTE()得到一个16bit数

最低(最右边)那个字节
    {
        WSACleanup();
        return;
    }
    SOCKET TcpClient = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN SerAddr;
    SerAddr.sin_family AF_INET;
    SerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    /*
    struct in_addr{
        union{
            struct{ u_char s_b1, s_b2, s_b3, s_b4;} S_un_b;
            struct{u_short S_w1, S_w2} S_un_w;
            u_long S_addr;
        }S_un;
    };
    */
    SerAddr.sin_port = htos(m_iPort);
    connect(TcpClient, (SOCKADDR*)&SerAddr, sizeof(SerAddr));  //与服务器端建立连接
    /*
    int connect(
        SOCKET s,  //服务器端Socket的识别码
        const struct sockaddr FAR* name,  //Socket想要连接的对方地址
        int namelen  //地址长度
    )
    */
    long FileLength;
    recv(TcpClient, (char *)&FileLength, sizeof(long), 0);
    /*
    int recv(
        SOCKET s,   
        char FAR *buf,   //存放接收到资料的暂存区
        int len,    //buf的长度
        int flags   //此函数的调用方式
    )
    */
    if(RecvFile("c:\\003.doc", TcpClient, FileLength))
        MessageBox("传输结束!\n");
    else
        MessageBox("传输失败!\n");
    CDialog::OnOK();
}

4>添加接收文件函数

BOOL CStreamSocketClientDlg::RecvFile(char *name, SOCKET conn, UINT filelen)
{
    char *FileName = name;
    SOCKET client = conn;
    CFile file;
    if(!file.Open(FileName, CFile::modeWrite))
    {
        printf("打开%s失败!\n", FileName);
        return FALSE;
    }
    int NumBytes;  //用来保存每次接收时数据块的大小
    UINT Total = 0;  //用来保存套接字已经接收的总的字节数
    int BufSize = 1024;  //接收缓冲区的大小
    int Size = BufSize;  //写文件的大小
    LPBYTE pBuf = new BYTE[BufSize];  //接收缓冲区
    DWORD dwTemp = 0;
    file.SeekToBegin();
    while(Total < filelen)
    {
        if((int)(filelen - Total) < Size)
            Size = filelen - Total;
        NumBytes = recv(client, (char *)pBuf, Size, 0);

        if(NumBytes == SOCKET_ERROR)
        {
            printf("接收失败!\n");
            return FALSE;
        }
        file.Write(pBuf, NumBytes);  
        /*
        virtual void Write(  
            const void* lpBuf,   //存储要写的东西的字符串或者字符数组
            UINT nCount );   //要从这个字符串或者字符数组中写多少个字符到文件中
        */
        Total += NumBytes;
        file.Seek(Total, CFile::begin);
    }
    delete[] pBuf;
    file.Close();
    closesocket(client);
    return TRUE;
}

界面:
利用流式套接字传输数据文件_第2张图片

利用流式套接字传输数据文件_第3张图片

你可能感兴趣的:(hack)