zoukankan      html  css  js  c++  java
  • C语言socket编程<一>socket之Winsock API

    参考网址:https://blog.csdn.net/Datura_Metel/article/details/79900395

    https://www.2cto.com/kf/201804/736240.html

    https://blog.csdn.net/m0_37947204/article/details/80489431

    https://blog.csdn.net/jinmie0193/article/details/78951055

    哈工大相关课程ppt

    小弟我学识浅薄,理解不到位之处请各位海涵,另请大哥提点提点!小弟我感激不尽!

    目标是一篇搞懂socket编程的所有api,一篇进行实战演练。

      首先解决下面几个问题:

      1.对socket编程的理解:

      也即是:调用一些操作系统提供的api,来与应用进程进行交互。

      1.微软公司在其操作系统中采用了套接字接口 API ,形成了一个稍有不同的 API,并称之为   Windows Socket Interface,WINSOCK。 

      2.Berkeley UNIX 操作系统定义了一种 API,称为 套接字接口(socket interface),简称套接字( socket)。 

      3.AT&T 为其 UNIX 系统 V 定义了一种 API,简写 为 TLI (Transport Layer Interface)。

      这里使用的是WINSOCK

      如何标识对外的通信端点呢? IP地址+端口号 

      操作系统/进程如何管理套接字(对内)?套接字描述符(socket descriptor)是一个小整数 。

      

       类似于文件的抽象

      当应用进程创建套接字时,操作系统分配一个数据结构存储该套接字相关信息

      返回套接字描述符 

      使用TCP/IP协议簇的网络应用程序声明端点地址变量时,使用结构 sockaddr_in.

      定义结构sockaddr_in:

    /*
    * Socket address, internet style.
    */
    struct sockaddr_in {
            short   sin_family;/*地址族(TCP/IP:AF_INET) */ 
            u_short sin_port;/*端口号   */  
            struct  in_addr sin_addr;/*IP地址   */ 
            char    sin_zero[8];/*未用(置0)   */ 
    };

      

    WSAStartup 

    使用Socket的应用程序在使用Socket之前必须首先调用 WSAStartup函数 

    两个参数:

     第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本、低位字节指明主版本.

      十六进制整数,例如0x102表示2.1版

    第二个参数返回实际的WinSock的版本信息

      • 指向WSADATA结构的指针 

    示例代码如下:使用2.1版本的WinSock的程序代码段 :

    wVersionRequested = MAKEWORD( 2, 1 );  //创建字,高字节是2,低字节是1,表示版本2.1
    err = WSAStartup( wVersionRequested, &wsaData ); //返回实际的WinSock的版本信息

    WSACleanup 

     

      应用程序在完成对请求的Socket库的使用, 最后要调用WSACleanup函数解除与Socket库的绑定并释放Socket库所占用的系统资源 

      不清理的话,在运行codeblocks的时候,常常可能会发生id.exe被占用错误。无法运行,需要去管理器找进程关掉才可以继续运行。

      此类型错误处理办法详见我其他博文。

    socket

     

      创建套接字,操作系统返回套接字描述符(sd)

      第一个参数(协议族): protofamily = PF_INET(TCP/IP)

      第二个参数(套接字类型): 

       type = SOCK_STREAM,SOCK_DGRAM or SOCK_RAW(TCP/IP)

      第三个参数(协议号):0为默认

      例:创建一个流套接字的代码段 

    struct protoent *p; 
     p=getprotobyname("tcp");  
    SOCKET sd=socket(PF_INET,SOCK_STREAM,p->p_proto);  

    TCP:可靠、面向连接、字节流传输、点对点

    UDP:不可靠、无连接、数据报传输 

    Closesocket  

    注意c语言中,如果你不管的话,就会一直保留着资源。。

    关闭一个描述符为sd的套接字

    如果多个进程共享一个套接字,调用closesocket 将套接字引用计数减1,减至0才关闭

    一个进程中的多线程对一个套接字的使用无计数

    如果进程中的一个线程调用closesocket将一个套接字 关闭,该进程中的其他线程也将不能访问该套接字

    返回值:

    0:成功 

    SOCKET_ERROR:失败

    bind 

     

    绑定套接字的本地端点地址 

    IP地址+端口号

    参数:

    套接字描述符(sd)

    端点地址(localaddr) • 结构sockaddr_in   

    客户程序一般不必调用bind函数

    服务器端?

     熟知端口号:

     IP地址? 地址通配符:INADDR_ANY

    参考网站:https://www.cnblogs.com/ok-lanyan/articles/2634242.html

    http://blog.chinaunix.net/uid-23193900-id-3199173.html

       有连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息,无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候打开端口。

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

      本来用的是TCP,客户端就不用绑定端口了,绑定之后只能运行一个client的程序属于自己人为设定的障碍,而从服务器那边得到的客户机连接端口号(是系统自动分配的)与这边客户机绑定的端口号根本是不相关的,所以客户端绑定也就失去了意义。

      如果bind了,就绑定了服务器的端口,也就导致了绑定之后只能运行一个client的程序。

    listen  

      置服务器端的流套接字处于监听状态

        仅服务器端调用 

        仅用于面向连接的流套接字

    设置连接请求队列大小(queuesize)

    返回值: 

       0:成功 

       SOCKET_ERROR:失败 

    connect

    客户程序调用connect函数来使客户套接字(sd)与特定计算机 的特定端口(saddr)的套接字(服务)进行连接

     仅用于客户端

     可用于TCP客户端也可以用于 UDP客户端

     TCP客户端:建立TCP连接

     UDP客户端:指定服务器端点地址

     

    accept

     服务程序调用accept函数从 处于监听状态的流套接字sd 的客户连接请求队列中取出排在最前的一个客户请求, 并且创建一个新的套接字来与客户套接字创建连接通道

     仅用于TCP套接字

     仅用于服务器

     利用新创建的套接字 (newsock)与客户通信

    send, sendto 

    send函数TCP套接字(客户与服务器)调用了 connect函数的UDP客户端套接字

      假如调用了connect了,自然就知道了特定端口套接字了,无需再声明destaddr了。

    sendto函数用于UDP服务器端套接字未调用 connect函数的UDP客户端套接字

       假如没有调用connect,自然就不知道特定端口套接字,需要声明destaddr,addrlen!!!

    我们这里需要指出一点:

      由于UDP是无连接的,你使用connect()进行了连接,他也只是发一个:/*我要开始连接了!*/给服务器,然后就开始自顾自地用sendto()或者send()发自己地请求。connect()中,调用TCP返回成功是真的建立了连接,但是UDP并不一定真正建立了连接!!!!

      UDP就像一个任性的人,他说,我要开始做某事了,于是就不论他人是否合作,自己干了起来。

    recv, recvfrom 

     

    recv函数从TCP连接的另一端接收数据,或者从调用了connect函数的UDP客户端套接字接收服务 器发来的数据

    假如调用了connect了,自然就知道了特定端口套接字了,无需再声明destaddr了。

    recvfrom函数用于从UDP服务器端套接字与未调 用connect函数的UDP客户端套接字接收对端数据

     类似同上。

    setsockopt, getsockopt 

     

    setsockopt()函数用来设置套接字sd的选项参数

    getsockopt()函数用于获取任意类型、任意状态套 接口的选项当前值,并把结果存入optval

    小结:

     

    一个容易忽略的问题:进行网络传输机器两端的“大端机器”和“小端机器“的处理:

    网络应用的Socket API(TCP)调用基本流程 

     到这里,主要使用的API就说完了。

  • 相关阅读:
    Information retrieval (IR class2)
    HTML随笔
    Evaluating Automatically Generated timelines from the Web (paper1)
    Kali 2020.1版本安装
    SystemTap
    Linux之IDIDID
    调试&内核探针
    Return-to-dl-resolve
    转载!
    一张图系列之函数重定位
  • 原文地址:https://www.cnblogs.com/hitWTJ/p/9886700.html
Copyright © 2011-2022 走看看