• Windows网络编程 2 【转】


    Windows网络编程使用winsock。Winsock是一个基于Socket模型的API,在Windows系统中广泛使用。
    使用Winsock进行网络编程需要包含头文件Winsock2.h,需要使用库ws2_32.lib,包含方法:可以使用语句来告诉编译器连接该库
    #pragma comment(lib, “ws2_32.lib”);
    如果使用VS,可以通过“项目” --> “XX属性”--> “连接器”-->“输入”--> “附加依赖项”添加ws2_32.lib。 (XX为当前工程名)

    面向连接的C/S程序工程流程图
    image


    使用Winsock API编制的网络应用程序中,在调用任何一个Winsock函数之前都必须检查协议栈安装情况,使用函数WSAStartup()完成操作。
    ● 一个服务端的例子

    复制代码
    void server::startServer()
    {
        WORD wVersionRequested;
        WSADATA wsaData;
        wVersionRequested=MAKEWORD(2,2);
        if(WSAStartup(wVersionRequested,&wsaData)!=0)
        {
            //Winsock初始化错误
            msgBox.exec();
            return;
        }
        if(wsaData.wVersion!=wVersionRequested)
        {
            //Winsock版本不匹配        
            WSACleanup();
            return;
        }
    
        if ((m_sk = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
        {        
            WSACleanup();
            return;
        }
        bool ok;
        unsigned short port = ui.portLineEdit->text().toInt(&ok, 10);
        if (ok == false)
        {
            //端口输入错误
            closesocket(m_sk);
            m_sk = -1;
            WSACleanup();
            return;
        }
    
        sockaddr_in addr;
        addr.sin_family = AF_INET; //使用互联网际协议,即IP协议
        addr.sin_port = htons(port);
        addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    
        if (bind(m_sk, (sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
        {
            //绑定端口失败
    
            closesocket(m_sk);
            m_sk = -1;
            WSACleanup();
            return;
        }
    
        if (listen(m_sk, 10) == SOCKET_ERROR)
        {
            //监听端口失败
            closesocket(m_sk);
            m_sk = -1;
            WSACleanup();
            return;
        }
        updateMsgRecs(tr("等待连接..."));
        //创建线程去等待连接
        HANDLE h = CreateThread(NULL, 0, &server::acceptProc, (LPVOID)this, 0, 0);
        if (h == NULL)
        {        
            closesocket(m_sk);
            m_sk = -1;
            WSACleanup();
            return;        
        }
        CloseHandle(h);
    }
    复制代码

    ● 监听线程函数

    复制代码
    DWORD WINAPI server::acceptProc(LPVOID lpParamter)
    {
        /*server *p_server = (server *)lpParamter;
        sockaddr_in client_addr;
        int len = sizeof(client_addr);
        char msgBuff[256];
        int sk;
        while (1)
        {
            if ((sk = ::accept(p_server->sk(), (sockaddr*)&client_addr, &len) )== SOCKET_ERROR)
            {
                emit p_server->haveNewMsg(QObject::tr("accept 出错"));
                break;
            }
            
            sprintf(msgBuff, "新连接来自%s", inet_ntoa(client_addr.sin_addr));
            emit p_server->haveNewMsg(msgBuff);
            emit p_server->newClient(inet_ntoa(client_addr.sin_addr), sk);
        }*/
    
        server *p_server = (server *)lpParamter;
        int client[FD_SETSIZE];
        fd_set allset, rset;
        sockaddr_in client_addr;
        int len;
        int clientfd;
        int sockfd;
    
        int i;
        for (i=0; i<FD_SETSIZE; i++)
            client[i] = -1;
        FD_ZERO(&allset);
        int listenfd = p_server->sk();
        FD_SET(listenfd, &allset);
    
        int nready;
        int maxfd = listenfd;
        int maxi = -1;
        char buff[MAX_LEN+1];
    
        while (1)
        {
            rset = allset;
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
            if (FD_ISSET(listenfd, &rset)) //new connection
            {
                len = sizeof(client_addr);
                if ( (clientfd = ::accept(p_server->sk(), (sockaddr*)&client_addr, &len)) == SOCKET_ERROR)
                {
                    emit p_server->haveNewMsg(QObject::tr("accept 出错"));
                    break;
                }
                //找出client数组中第一个为-1的单元存放已经连接的socket
                for (i=0; i<FD_SETSIZE; i++)
                {
                    if (client[i] <0)
                    {
                        client[i] = clientfd;
                        break;
                    }
                }
                if (i == FD_SETSIZE)
                {
                    emit p_server->haveNewMsg(QObject::tr("error: too many clients!"));
                    break;
                }
                //sprintf(buff, "新连接来自%s", inet_ntoa(client_addr.sin_addr));
                //emit p_server->haveNewMsg(buff);
                emit p_server->newClient(inet_ntoa(client_addr.sin_addr), clientfd);
                FD_SET(clientfd, &allset);
                if (clientfd > maxfd)
                    maxfd = clientfd;
                if (i>maxi)
                    maxi = i;
                if (--nready <= 0)
                    continue;
            }
            for (i=0; i<=maxi; i++)
            {
                if ( (sockfd = client[i]) < 0)
                    continue;
                if (FD_ISSET(sockfd, &rset))
                {
                    int n;
                    //客户端已经关闭连接
                    if ( (n = recv(sockfd, buff, MAX_LEN, 0 )) <= 0)
                    {
                        closesocket(sockfd);
                        FD_CLR(sockfd, &allset);
                        client[i] = -1;
                        emit p_server->haveNewMsg(QObject::tr("client closed"));
                        emit p_server->sClientClose(sockfd);
                    }
                    else //收到数据
                    {
                        buff[n] = 0;
                        emit p_server->haveNewMsg( buff, sockfd);
                        if (--nready <= 0)
                            break;
                    }
                }
            } //for (i=0; i<=maxi; i++)
        }
        return 0;
    }
    复制代码

      一个客户端的例子

    复制代码
    void client::connectServer()
    {
        WORD wVersionRequested;
        WSADATA wsaData;
        wVersionRequested=MAKEWORD(2,2);
        if(WSAStartup(wVersionRequested,&wsaData)!=0)
        {
            //Winsock初始化错误
            QMessageBox msgBox(QMessageBox::Warning, tr("错误"), tr("Winsock初始化错误"), QMessageBox::Ok, 0);
            msgBox.exec();
            return;
        }
        if(wsaData.wVersion!=wVersionRequested)
        {
            //Winsock版本不匹配
            QMessageBox msgBox(QMessageBox::Warning, tr("错误"), tr("Winsock版本不匹配"), QMessageBox::Ok, 0);
            msgBox.exec();
            WSACleanup();
            return;
        }
    
        if ((m_sk = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
        {
            QMessageBox msgBox(QMessageBox::Warning, tr("错误"), tr("创建socket失败"), QMessageBox::Ok, 0);
            msgBox.exec();
            WSACleanup();
            return;
        }
    
        sockaddr_in addr;
        addr.sin_family = AF_INET;
    
        bool ok;
        unsigned short port = ui.portLineEdit->text().toInt(&ok, 10);
        if (ok == false)
        {
            QMessageBox msgBox(QMessageBox::Warning, tr("错误"), tr("端口输入错误"), QMessageBox::Ok, 0);
            msgBox.exec();
            closesocket(m_sk);
            m_sk = -1;
            WSACleanup();
            return;
        }
        addr.sin_port  = htons(port);
        addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    
        if (0 == ::connect(m_sk, (sockaddr *)&addr, sizeof(addr)))
        {
            updateMsgRecs(tr("连接成功..."));
            HANDLE h = CreateThread(NULL, 0, &client::recvProc, (LPVOID)this, 0, 0);
            CloseHandle(h);
        }
        else
        {
            QMessageBox msgBox(QMessageBox::Warning, tr("错误"), tr("连接出错"), QMessageBox::Ok, 0);
            msgBox.exec();
            closesocket(m_sk);
            m_sk = -1;
            WSACleanup();
            
        }
    }
    复制代码

    ●  winsock APIs

    网络连接函数
    socket 创建套接字
    bind 绑定本机端口
    connect 建立连接
    listen 监听端口
    accept 接受连接
    recv, recvfrom 数据接收
    send, sendto 数据发送
    close, shutdown 关闭套接字
    转换函数
    inet_addr() 点分十进制数表示的IP地址转换为网络字节序的IP地址
    inet_ntoa() 网络字节序的IP地址转换为点分十进制数表示的IP地址
    字节顺序转换函数
    htonl 4字节主机字节序转换为网络字节序
    ntohl  4字节网络字节序转换为主机字节序
    htons 2字节主机字节序转换为网络字节序
    ntohs 2字节网络字节序转换为主机字节序
    网络信息检索函数
    gethostname 获得主机名
    getpeername 获得与套接口相连的远程协议地址
    getsockname 获得套接口本地协议地址
    gethostbyname 根据主机名取得主机信息
    gethostbyaddr 根据主机地址取得主机信息
    getprotobyname 根据协议名取得主机协议信息
    getprotobynumber 根据协议号取得主机协议信息
    getservbyname 根据服务名取得相关服务信息
    getservbyport 根据端口号取得相关服务信息
    getsockopt/setsockopt 获取/设置一个套接口选项
    ioctlsocket 设置套接口的工作方式

  • 相关阅读:
    2017.10.25总结与回顾
    2017.10.24总结与回顾
    CSS盒子模型
    2017.10.23学习知识总结回顾及编写新网页
    JAVA与mysql之间的编码问题
    想写好代码,送你三个神器
    Vue+highligh.js实现语法高亮(转)
    Vue.JS实现复制到粘帖板功能
    npm install、npm install --save与npm install --save-dev区别(转)
    ES7与ES8新特性
  • 原文地址:https://www.cnblogs.com/mazhenyu/p/4516828.html
Copyright © 2020-2023  润新知