当前位置:   article > 正文

socket应用编程实验(一)_socket编程实验

socket编程实验

Socket应用编程实验(1)

  • Socket简介

  • HTTP/HTTPS协议

  • Socket编程实验

    Socket API

  • BSD Socket API

    • 不是为每个应用程序定义接口,而是提供最基本的通信功能

    • 对上层提供统一的调用接口,支持丰富的上层应用开发

      //=====================BSD Socket API=======================
      socket(domain,type,proto);
      close(sockfd);
      bind(sockfd,addr,addrlen);
      //=============Datagram(Connectionless)======================
      sendto(sockfd,buf,len,flags,dest_aaddr,addrlen);
      recvfrom(sockfd,buf,len,flags,src_addr,addrlen);
      //=============Stream(Connection-oriented)===================
      listen(sockfd,backlog);
      accept(sockfd,addr,&addrlen);
      connect(sockfd,addr,addren);
      send(sockfd,buf,len,flags);
      recv(sockfd,&buf,len,flags);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
  • Socket调用流程

在这里插入图片描述

一、建立socket文件描述符

  • 数据收发两端都需要建立socket文件描述符

    # include<sys/socket.h>
    int socket(int domain,int type,int protocol);
    
    //在Linux 下使用<sys/socket.h>头文件中的socket()函数来创建套接字,原型为
    int socket(int af,int type,int protocol);
    
    1) af为地址族(Address Family),也就是IP地址类型,常用的有 AF_INET 和AF_INET6。
    AF是"address Family"的简写,INET是"Inetnet"的简写。AF_INET表示IPv4地址,例如
    127.0.0.1;AF_INET6表示IPv6地址,例如 1030::C9B4:FF12:48AA:1A2B.
    
    127.0.0.1是一个特殊的IP地址,表示本机地址
        
    
    2) type为数据传输方式/套接字类型,常用的有 SOCK_STREAM (流格式套接字/面向连接的套接字)SOCK_DGRAM (数据报套接字/无连接的套接字)3) protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP, 分别表示 TCP 传输协议和 UDP传输协议。
    
     int socket(IP地址类型(IPv4/IPv6), 数据传输方式(SOCK_STREAM / SOCK_DGRAM), 传输协议(IPPROTO_TCP/IPPTOTO_UDP))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 第三个参数,一般情况下有了af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到: 有两种不同的协议支持同一种地址类型和数据传输类型。如果不指明,操作系统无法自动推演。

      /* 使用IPv4地址,参数af的值为 AF_INET ,如果使用 SOCK_STREAM 传输数据,那么满足
      这两个条件的协议只有 TCP,因此可以这样调用 socket()函数:  */
      int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      /* 使用SOCK_DRGAM 传输方式,满足这两个条件的协议只有 UDP*/
      int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      /* 上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为0,系统会自动推演出应该使用什么协议 */
      int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);	// 创建TCP套接字
      int udp_socket = socket(AF_INET, SOCK_DRGAM, 0);	// 创建UDP套接字
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • Domain: AF_INET

      • Type

        • SOCK_STREAM TCP

        • SOCK_DGRAM UDP

          • Protocol: 0

          • 返回值: socket 文件描述符

            int sockfd = socket(AF_INET, SOCK_STREAM, 0);
            
            • 1

        二、将socket文件描述符与监听地址绑定

    bind()函数功能

    将 address 指向的 sockaddr 结构体中描述的一些属性(IP地址、端口号、地址簇) 与 socket套接字绑定,也叫给套接字命名

    调用bind()后,就为 sockfd 套接字关联了一个相应的地址与端口号,即发送到该地址该端口的数据可以通过 sockfd 读取和使用。当然也可通过该 sockfd 发送数据到指定目的。

    对于 Server, bind()是必须要做的事情,服务器启动时需要绑定指定的端口来提供服务(以便于客户向指定的端口发送请求),对于服务器 sockfd 绑定地址,一般而言将 IP 地址复制为 INADDR_ANY(该宏值为 0),即无论发送到系统中的哪个 IP 地址(当服务器有多张网卡时会有多个 IP 地址)的请求都采用该 sockfd 来处理,而无需指定固定 IP。

    对于 Client , 一般而言无需主动调用 bind(),一切由操作系统来完成。在发送数据前,操作系统会为套接字随机分配一个可用的端口,同时将该套接字和本地地址信息绑定。

    • 只需要被动建立连接一方进行绑定(bind)

      int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      sockfd : Socket 文件描述符
      addr :  需要绑定的地址和端口
      addrlen : 地址和端口数据结构的长度
      
      • 1
      • 2
      • 3
      • 4
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(80);  // 主机字节序调整为网络字节序,80端口是http协议
    
    bind(sockfd,(struct sockaddr *)&server, sizeof(server));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

三、进行监听

  • 只在被动建立连接一方进行监听,等待新的连接请求

    int listen(int sockfd, int backlog)
     // backlog 指最多允许多少个客户连接到服务器,它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求。
    
    • 1
    • 2
  • sockfd: 之前建立的文件描述符

  • backlog: 可以理解为待处理请求的最大数目

    listen(sockfd,128);
    
    • 1

四、接受连接请求

accept() 接收一个套接字中已建立的连接

# include<sys/types.h>
# include<sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 1
  • 2
  • 3
  • 功能参数描述

accept()系统调用主要用在基于连接的套接字类型 ,比如 SOCK_STREAM 和 SOCK_SEQPACKET。**它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符。**新建立的套接字不在监听状态,原来所监听的套接字也不受该系统调用的影响。新建立的套接字准备发送send()和接收数据recv()。

  • 参数

  • sockfd, 利用系统调用socket()建立的套接字描述符,通过bind()绑定到一个本地地址(一般为服务器的套接字),并且通过listen()一直在监听连接。

  • addr : 指向struct sockaddr 的指针,该结构用**通讯层服务器对等套接字的地址(一般为客户端地址)**填写,返回地址 addr 的确切格式由套接字的地址类别(比如TCP或者UDP)决定;若addr为NULL,没有有效地址填写,这种情况下,addrlen也不使用,应该置为 NULL;

    addr 是个指向局部数据结构 sockaddr_in 的指针,这就是要求接入的信息本地的套接字(地址和指针)。

  • addrlen: 一个值结果参数,调用函数必须初始化为包含 addr 所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际数值;

    addrlen 是个局部整型变量,设置为sizeof(struct sockaddr_in)

    如果队列中没有等待的连接,套接字也没有被标记为Non-blocking,accept()会阻塞调用函数直到连接出现; 如果套接字被标记为 Non-blocking,队列中也没有等待的连接, accept()返回错误

    EAGAIN 或 WWOULDBLOCK。

    • 一般来说,实现时 accept()为阻塞函数,当监听sockfd 调用 accept()时,它先到自己的 receive_buf 中查看是否有连接数据包;
    • 若有,把数据拷贝出来,删掉接收到的数据包,创建新的socket 与客户发来的地址建立连接;
    • 若没有,就阻塞等待;

​ 为了在套接字中有到来的连接时得到通知,可以使用select() 或 poll() 。当尝试建立新连接时,系统发送一个可读事件,然后调用accept()为该连接获取套接字。另一种方法是,当套接字中有连接到来时设定套接字发送SIGIO信号。

  • 返回值:成功:返回非负整数,该整数是接收到套接字的描述符;出错返回-1,相应设定全局变量errno。

    被动建立连接一方需要显式的接收连接请求
     int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    sockfd: 之前建立的 socket文件描述符
    addr : 用于存储对端网络地址的数据结构
    addrlen : 指定 addr 大小
    返回值为该连接对应的文件描述符,以后收发数据都使用该文件描述符:
    int csock = accept(sockfd, (struct sockaddr_in *)&caddr, &clen);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/143954
推荐阅读
相关标签
  

闽ICP备14008679号