Windows网络编程(基础篇1)

  1. Winsock是一种网络编程接口,不是协议。
  2. 除了WSAStartup、WSACleanup、WSARecvEx、WSAGetLastError属于Winsocket1.1规范函数外,凡是有前缀WSA的,都是在Winsock 2 中更新或者增添的一个新的API函数。

一、Winsock初始化

  1. 包含头文件winsock2.h,链接库WS2_32

    include <winsock2.h>#pragma comment(lib,"WS2_32")
    
  2. 使用Winsock的应用都必须加载合适的Winsock DLL版本,否则返回SOCKET_ERROR。使用WSAStartup加载,最后需要调用WSACleanup释放Winsock分配的资源。

    int WSAStartup(_In_  WORD      wVersionRequested,_Out_ LPWSADATA lpWSAData
    );
    • wVersionRequested:版本号,高阶字节指定小版本号,低位字节指定主版本。
    • lpWSAData 指向WSADATA数据结构的,接收Windows Sockets实现细节。
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);//成功返回0

    MAKEWORD:创建一个无符号16位整形,通过连接两个给定的无符号参数,也就是将(2,2)放入wVersionRequested中。

    WSAGetLastError();//返回调用winsock函数发生的错误代码
    WSACleanup();//程序结束时,需要调用释放资源

二、SOCKADDR_IN简介

  1. SOCKADDR_IN:用来指定IP地址和端口信息。

    typedef struct sockaddr_in {short   sin_family;          //The address family for the transport address,must AF_INETUSHORT sin_port;         //port numberIN_ADDR sin_addr;            // IPv4 transport addressCHAR sin_zero[8];            //Reserved(预留) for system use
    } SOCKADDR_IN, *PSOCKADDR_IN;
  2. inet_pton 转换字符串到网络地址。将“点分十进制” -> “二进制整数”(inet_addr已弃用)

    //m_HostGroup.sin_addr.s_addr = inet_addr(strGroupIP);//代替方法如下:

    inet_pton(AF_INET, strGroupIP, (void*)&m_HostGroup.sin_addr.s_addr);

    INT WSAAPI InetPton(_In_  INT     Family,          // AF_INET and AF_INET6._In_  PCTSTR pszAddrString,    //待转换的地址,IPV4 或 IPV6_Out_ PVOID  pAddrBuf          //转换后的(IPV4:IN_ADDR,IPV6: IN6_ADDR
    );
  3. htons 将整型变量从主机字节顺序转变成网络字节顺序

    u_short WSAAPI htons(_In_ u_short hostshort);
  4. ntohl 将网络字节顺序转换成主机字节顺序

    u_long WSAAPI ntohl(_In_ u_long netlong);
    • 创建SOCKADDR_IN结构示例:
    SOCKADDR_IN sin;
    WORD Port=80;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(Port);
    inet_pton(AF_INET, "192.168.1.1", (void*)&sin.sin_addr.S_un.S_addr);

三、套接字通信(TCP)

  1. 创建套接字函数:socket、WSASocket

    SOCKET WSAAPI socket(_In_ int af,           //指定协议族 AF_INET、AF_INET6、AF_LOCAL等_In_ int type,     //指定Socket类型 (TCP)SOCK_STREAM、(UDP)SOCK_DGRAM、SOCK_RAW等_In_ int protocol      //指定协议 IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP等
    );
  2. 创建套接字后,就必须将套接字绑定到一个已知地址上:bind

    int bind(_In_ SOCKET                s,          //待连接的套接字_In_ const struct sockaddr *name,      //地址缓冲区(sockaddr *)sockaddr_in_In_ int                   namelen //name大小
    );
  3. 将套接字置入监听模式:listen;(bind只是将套接字和指定地址关联,listen指示套接字等候连接)

    int listen(_In_ SOCKET s,         //待监听的套接字_In_ int    backlog        //等待连接队列的最大长度
    );
  4. 有客户端连接到达时,接收一个连接:accept

    SOCKET accept(_In_    SOCKET          s,     //正在监听的套接字_Out_   struct sockaddr *addr, //连接者的地址_Inout_ int             *addrlen   //指向存有addr地址长度的整数
    );
  5. 客户端通过套接字连接到服务端:connect

    int connect(_In_ SOCKET                s,  _In_ const struct sockaddr *name,_In_ int                   namelen
    );
  6. 服务端示例:

    
    #include<winsock2.h>#pragma comment(lib,"WS2_32")int main(void)
    {
    WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {return 0;
    }
    SOCKET ListeningSocket;
    SOCKET NewConnetction;
    SOCKADDR_IN ServerAddr;
    SOCKADDR_IN ClientAddr;
    int Port = 5150;//创建一个套接字来监听客户端连接
    ListeningSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(Port);
    ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;//用bind将套接字信息和地址信息绑定
    ::bind(ListeningSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));//监听客户连接,限制5个
    ::listen(ListeningSocket, 5);//连接到达时,接受一个新连接
    int ClientaddrLen;
    NewConnetction = ::accept(ListeningSocket, (SOCKADDR*)&ClientAddr, &ClientaddrLen);//此时在这些套接字上可以做:
    //1.在ListeningSocket上再次调用accept,等待更多的连接。2.在NewConnection上完成数据收发。//关闭套接字
    closesocket(NewConnetction);
    closesocket(ListeningSocket);WSACleanup();
    return 1;
    }
  7. 客户端示例:

    
    #include<winsock2.h>#include<Ws2tcpip.h>#pragma comment(lib,"WS2_32")int main(void)
    {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {return 0;
    }
    SOCKET S;
    SOCKADDR_IN ServerAddr;
    int Port = 5150;//创建一个套接字来建立客户端连接
    S = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(Port);
    inet_pton(AF_INET, "192.168.1.1", (void*)&ServerAddr.sin_addr.S_un.S_addr);
    connect(S, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));//关闭套接字
    closesocket(S);
    WSACleanup();
    return 1;
    }
  8. 为什么客户端不用bind

    • 无连接的socket的客户端和服务端以及面向连接socket的服务端通过调用bind函数来配置本地信息。使用bind函数时,通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。
    • 有连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息,无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候打开端口。
    • 服务端进程bind IP地址:目的是限制了服务端进程创建的socket只接受那些目的地为此IP地址的客户链接

    1.需要在建连前就知道端口的话,需要 bind
    2.需要通过指定的端口来通讯的话,需要 bind

四:数据传输

  1. 要在已经建立的套接字上发送数据,可以用send和WSASend。接收数据可以用recv和WSARecv。

    int send(_In_       SOCKET s,       //是一个已经建立了连接,用于发送数据的套接字_In_ const char   *buf,    //指向即将发送数据的缓冲区_In_       int    len, //缓冲区内的字符数_In_       int    flags    //调用执行方式
    );//如果成功,返回的是发送的字节数,否则返回SOCKET_ERROR
    int WSASend(_In_  SOCKET                             s,_In_  LPWSABUF                           lpBuffers,_In_  DWORD                              dwBufferCount,_Out_ LPDWORD                            lpNumberOfBytesSent,_In_  DWORD                              dwFlags,_In_  LPWSAOVERLAPPED                    lpOverlapped,_In_  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    );

    char sendbuff[2048];int nBytes=2048; sendbuff=******;

    ret=send(s,sendbuff,nBytes,0);

    对于send函数而言,可能返回已发出的字节数少于给定的字节数。因为对于每个收发数据的套接字来说,系统都为他们分配了相当充足的缓冲区空间,所以返回值ret变量将被设为已发送的字节数。在发送数据时,内部缓冲区都会将数据一致保留到可以将它发到线上为之。比如,传输大量的数据可以领缓冲区快速填满。同时,对TCP/IP来说,还有一个窗口大小的问题。接收端会对窗口大小进行调节,以指示它可以接收多少数据。如果有大量数据涌入接收端,接收端就会将窗口大小设为0,为挂起数据做好准备。对发送端来说,这样会强制它在收到一个新的大于0的窗口大小之前,不得再发送数据。在使用send调用时,缓冲区可能只能容纳1024字节,这时,便有必要重新提交剩下的1024字节。

    char sendbuff[2018]; int nBytes=2048, nLeft, idx;
    nLeft=nBytes;    idx=0;
    while(nLeft>0){ret=send(s,&sendbuff[idx],nLeft,0);if(ret==SOCKET_ERROR){//error}nLeft-=ret;idx+=ret;}

  2. 在已经建立连接的套接字上接受数据的传入,可以使用recv和WSARecv。

    int recv(_In_  SOCKET s,_Out_ char   *buf,_In_  int    len,_In_  int    flags
    );
    int WSARecv(_In_    SOCKET                             s,_Inout_ LPWSABUF                           lpBuffers,_In_    DWORD                              dwBufferCount,_Out_   LPDWORD                            lpNumberOfBytesRecvd,_Inout_ LPDWORD                            lpFlags,_In_    LPWSAOVERLAPPED                    lpOverlapped,_In_    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    );
    

    流套接字是一个不间断的数据流,在读取它时,应用程序通常不会关心应该读多少数据。如果所有消息长度都一样,则处理要简单,比如读取512字节。

    char recvbuff[1024]; int ret, nLeft, idx;
    nLeft=512;
    idx=0;
    while(nLeft>0){ret=recv(s,&recvbuff[idx],nleft,0);if(ret==SOCKET_ERROR){//error}idx+=ret;nLeft-=ret;}

    如果消息长度不同,就必须要利用自己的协议来通知接收端,让它知道即将到来的消息长度是多少。比如,写入接收端的前4个字节总是整数,用来标记即将到来的消息长度。

  3. 中断连接

    一旦完成了套接字连接,就必须将它关掉,并释放关联到那个套接字句柄的所有资源,执行closesocket即可。但是,closesocket可能带来的负面影响就是导致数据丢失。鉴于此,在调用closesocket函数之前,利用shutdown函数从容中止连接。

    int shutdown(_In_ SOCKET s,_In_ int    how    //SD_RECEIVE,SD_SEND, SD_BOTH
    );//关闭一个套接字

    对closesocket调用释放的套接字描述符,如果再次利用该套接字就会调用失败。如果没有对改套接字的其他引用,那么所有与套接字描述符相关的资源都会被释放,包括丢弃所有队列中的数据。

Windows网络编程(基础篇1)相关推荐

  1. 万物互联之~网络编程基础篇

    入门篇¶ 官方文档:https://docs.python.org/3/library/ipc.html(进程间通信和网络) 实例代码:https://github.com/lotapp/BaseCo ...

  2. Java网络编程基础_Java网络编程基础篇

    一.前言 网络通讯在系统交互中是必不可少的一部分,无论是面试还是工作中都是绕不过去的一部分,本节我们来谈谈Java网络编程中的一些知识,本chat内容如下: 网络通讯基础知识,剖析网络通讯的本质和需要 ...

  3. Windows网络编程(一)基础

    Table of Contents 准备工作 socket C/S模式 源代码 服务端 客户端 源码分析 数据传输 关闭连接 符号解释 WSAStartup sin_family sin_port i ...

  4. Windows黑客编程基础

    俗话说:"万事开头难",编程也不例外,初学者如何入门关键要有一份正确的理论作指 导,下面的这篇文章虽不能说是至理名言,但我相信通过作者细腻的分析.讲解和引导, 定能给初学者起到启蒙 ...

  5. python网络编程证书_《Python网络编程基础》笔记

    python网络编程基础 ================== Author: lujun9972 Date: 2013-03-08 22:29:20 CST Table of Contents == ...

  6. [内核编程] 内核环境及其特殊性,驱动编程基础篇

    [内核编程] 内核环境及其特殊性,驱动编程基础篇  在学习汉江独钓一书后,打算总结一下内核编程应该注意的事项,以及有关的一些基础知识.第一次接触内核编程,还真是很生疏,很多东西不能一下马上消化.这里做 ...

  7. java 编程原理_Java网络编程 -- 网络编程基础原理

    Hello,今天记录下 Java网络编程 --> 网络编程基础原理. 一起学习,一起进步.继续沉淀,慢慢强大.希望这文章对您有帮助.若有写的不好的地方,欢迎评论给建议哈! 初写博客不久,我是杨展 ...

  8. Python3——网络编程基础

    Python3--网络编程基础 基础知识参考: https://blog.csdn.net/wqx521/article/details/51037048 https://blog.csdn.net/ ...

  9. 多实例多进程网络编程PHP,php socket网络编程基础知识(四):多进程

    标签:status   传递   windows   返回   修改   队列   _for   响应   关联 说明 php在web编程时是不需要考虑多进程的,但整个php流程是涉及到多进程的,只不 ...

  10. Windows驱动编程基础教程

    前言     本书非常适合熟悉Windows应用编程的读者转向驱动开发.所有的内容都从最基础的编程方法入手.介绍相关的内核API,然后举出示范的例子.这本书只有不到70页,是一本非常精简的小册子.所以 ...

最新文章

  1. spring boot初学习的数据库依赖
  2. Android -- 多线程下载
  3. 计算机硬件2部件指的是什么,计算机基础-2.计算机硬件基础.doc
  4. Springboot集成cache的key生成策略
  5. hdu 4091 线性规划
  6. 【MATLAB统计分析与应用100例】案例003:matlab调用smooth函数进行加噪数据的平滑处理
  7. php 5.6 文档,文件存储 | 进阶系列 | Laravel 5.6 中文文档
  8. 调试寄存器:Debug Register
  9. mapreduce程序调用各个类的功能
  10. 计算机毕业设计Java在线小说系统(源码+系统+mysql数据库+Lw文档)
  11. cad怎么画立体图形教学_立体图形怎么画步骤 找CAD图形中心点的方法步骤图
  12. 详解MOVE PROTOCOL的测试版,让健康运动如影随形
  13. Contrast Preserving Decolorization
  14. SSM框架搭建简单实例
  15. QT-3-基本组件2
  16. flex 垂直方向 两端对齐
  17. ionic升华过程8-cordova插件+mui小案例
  18. 为远控添加功能[1]
  19. Premiere插件大全介绍知羽,意匠,爱维,
  20. 基于Simulink的永磁同步电机仿真控制系统

热门文章

  1. 软考中级考试经验分享-系统集成项目管理工程师
  2. android模拟器命令行,夜神安卓模拟器命令行整理贴
  3. mnist数据集下载——mnist数据集提供百度网盘下载地址
  4. 父与子一起学python3_父与子的编程之旅(与小卡特一起学Python第3版全彩印刷)/图灵程序设计丛书...
  5. java dump 工具_Java内存Dump文件查看和分析工具介绍
  6. Adobe Acrobat Reader离线安装包下载
  7. hit网络安全实验报告
  8. flyMcu给STM32串口烧录失败踩坑、总结及注意事项
  9. ngrok跟小米球的使用
  10. xml样本标签转txt