• 网络编程--接口函数


    1.socket函数
    为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型
    #include <sys/socket.h>
    int socket (int family, int type, int protocol);
    //返回:若成功则为非负描述符,若出错则为-1
    其中family指明协议族,type参数指明套接字类型,protocol参数应该设为某个(见下图)协议类型常值,或者设为0,以选择所给定family和type组合的系统默认值
     
    socket函数的family常值
    family 说 明
    AF_INET
    AF_INET6
    AF_LOCAL
    AF_ROUTE
    AF_KEY
    IPv4协议
    IPv6协议
    Unix域协议
    路由套接口
    密钥套接口
    socket函数的type常值
    type 说 明
    SOCK_STREAM
    SOCK_DGRAM
    SOCK_SEQPACKET
    SOCK_RAW
    字节流套接口
    数据报套接口
    有序分组套接口
    原始套接口
     socket函数的protocol常值
    protocol 说 明
    IPPROTO_TCP
    IPPROTO_UDP
    IPPROTO_SCTP
    TCP传输协议
    UDP传输协议
    SCTP传输协议
    socket函数中family和type参数的组合
    2.connect函数
    TCP客户用connect函数来建立与TCP服务器的连接
    #include <sys/socket.h>
    int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
     //返回:若成功则为0,若出错则为-1
    sockfd是socket函数返回的套接字描述符,剩下的2个参数分别是一个指向套接字地址结构的指针和该结构的大小。connect函数将激发TCP的三次握手过程,而且仅在连接建立成功或出错时才返回,其中出错有如下几种情况:
    1).若TCP客户没有收到SYN包的响应,则返回ETIMEDOUT错误。如调用该函数时,内核发送一个SYN,若无响应则等待6s后再发一个,若仍无响应,则等待24s再发一个,若总共等了75s后仍未收到响应消息则返回该错误(因内核而异)。
    2).若响应时RST,表明该服务器主机在我们指定的端口上没有进程等待,客户收到RST包后马上返回ECONNREFUSED错误。
    3).若客户发出的SYN在中间的路由器上引发了一个“destination unreachable”的ICMP错误,则按第一种情形继续发送SYN,若在规定的时间内没有收到回应,则将ICMP错误作为EHOSTUNREACH或ENETUNREACH错误返回。
     
    3.bind函数
    bind函数把一个本地协议地址赋予一个套接字。对于网际协议,协议地址是一个ip地址和一个端口号
    #include <sys/socket.h>
    int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
    //返回,成功为0,出错为-1
    参数sockfd是socket函数返回的套接字描述符,myaddr是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度,对于TCP,调用bind函数可以指定一个端口,或者指定一个地址,也可以两者都指定,还可以都不指定:
    • 服务器在启动时候捆绑他们众所周知的端口
    • 进程可以把一个特定的IP地址捆绑到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一
       其中对于IPv4来说,通配地址常值INADDR_ANY来指定,其值一般为0,它通知内核选择IP地址
    4.listen函数
    函数listen 仅被TCP服务器调用,它做两件事件:
    1).当函数socket创建一个套接口时,它被假设为一个主动套接口,也就是说,它是一个将调用connect发起连接的客户套接口,函数listen将未连接的套接口转换成被动套接口,指示内核应接受指向此套接口的连接请求,
    2).函数的第二个参数规定了内核为此套接口排队的最大连接个数
    #include <sys/socket.h> 
    int listen (int sockfd, int backlog);
    //返回,成功为0,出错-1 
    要理解backlog参数,我们要知道内核为任何一个给定的监听套接字维护2个队列:
    1).未完成连接队列。客户和服务器之间的tcp三次握手并未完成。
    2).已完成连接队列。tcp的三次握手已经完成,处于ESTABLISHED状态。

    关于两个队列的处理:
    • listen函数的backlog参数曾被规定为两个队列总和的最大值
    • 源自Berkeley的实现给backlog增设了一个模糊因子,把它乘以1.5得到未处理队列最大长度
    • 不要把backlog定义为0,因为不同的实现对此有不同的解释
    • 在三路握手正常完成的前提下(也就是说没有丢失分节,从而没有重传),未完成连接队列的任何一项在其中的存留时间就是一个RTT,而RTT的值取决于特定的客户与服务器
    • 当一个客户SYN到达时,若这些队列是满的,TCP就忽略该分节,也就是不发送RST
    • 在三路握手完成后,但在服务器调用accept之前到达的数据应由服务器TCP排列,最大数据量为相应已连接套接字的接受缓冲区大小
    5.accept函数
    accept函数由TCP服务器调用,用于从已完成连接队列列头返回下一个已完成连接,如果已完成连接队列为空,进程将被投入睡眠(如果套接字为默认的阻塞方式)
    #include <sys/socket.h>
    int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
    //返回:若成功为非负描述符,出错为-1
    参数cliaddr和addrlen返回已连接的客户的协议地址,如果对客户的协议地址不感兴趣,可以置为空,参数addrlen在函数调用的时候是传入的套接字地址结构的大小,函数返回时它的值是内核存放在该套接字地址结构中的确切字节数。
    如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与返回客户的TCP连接,一般我们称accept函数第一个参数为监听套接字描述符(由socket创建,随后用作bind和listen的第一个参数的描述符),称它的返回值为已连接套接字描述符
    accept 函数最多返回三个值:一个既可能是新的套接字描述符也可能是出错指示的整数、客户进程的协议地址(由cliaddr指针所指)、以及该地址的大小(由addrlen指针所指)。
     
    TCP cs模型

    server.c

    #include <sys/types.h>      
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    
    #define SERVER_PORT 5555 
    #define MAXLINE 4096
    int main(void)
    {
        struct sockaddr_in serveraddr, clientaddr;
        int sockfd, addrlen, confd, len, i;
        char ipstr[128];
        char buf[MAXLINE];
    
        //1.socket
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        //2.bind
        bzero(&serveraddr, sizeof(serveraddr));
        /* 地址族协议IPv4 */
        serveraddr.sin_family = AF_INET;
        /* IP地址 */
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        serveraddr.sin_port = htons(SERVER_PORT);
        bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
        //3.listen
        listen(sockfd, 128);
        while (1) {
            //4.accept阻塞监听客户端链接请求
            addrlen = sizeof(clientaddr);
            confd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
            //输出客户端IP地址和端口号
            printf("client ip %s	port %d
    ", 
                    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, ipstr, sizeof(ipstr)),
                    ntohs(clientaddr.sin_port));
    
            //和客户端交互数据操作confd
            //5.处理客户端请求 
            len = read(confd, buf, sizeof(buf));
            i = 0;
            while (i < len) {
                buf[i] = toupper(buf[i]);
                i++;
            }
            write(confd, buf, len);
    
            close(confd);
        }
        close(sockfd);
    
        return 0;
    }

    client.c

    #include <netinet/in.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <fcntl.h>
    #define SERVER_PORT 8000
    #define MAXLINE 4096
    int main(int argc, char *argv[])
    {
        struct sockaddr_in serveraddr;
        int confd, len;
        char ipstr[] = "192.168.6.254";
        char buf[MAXLINE];
        if (argc < 2) {
            printf("./client str
    ");
            exit(1);
        }
        //1.创建一个socket
        confd = socket(AF_INET, SOCK_STREAM, 0);
        //2.初始化服务器地址
        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        //"192.168.6.254"
        inet_pton(AF_INET, ipstr, &serveraddr.sin_addr.s_addr);
        serveraddr.sin_port  = htons(SERVER_PORT);
        //3.链接服务器
        connect(confd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    
        //4.请求服务器处理数据
        write(confd, argv[1], strlen(argv[1]));
        len = read(confd, buf, sizeof(buf));
        write(STDOUT_FILENO, buf, len);
    
        //5.关闭socket
        close(confd);
        return 0;
    }

    Makefile

    all:server client
    
    
    server:server.c
        gcc $< -o $@
    
    client:client.c
        gcc $< -o $@
    
    
    .PHONY:clean
    clean:
        rm -f server
        rm -f client

    6.数据报接受与发送

    #include <sys/socket.h>
    ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); 
    ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen);
    //均返回:若成功则为读或写的字节数,出错为-1
    默认情况recvfrom函数没有接收到对方数据时候是阻塞的

    UDP cs模型

    server.c

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <netinet/in.h>
    #include <ctype.h>
    
    #define SERVER_PORT 5556 
    #define MAXLINE 1024 
    int main(void)
    {
        int sockfd,i;
        ssize_t len;
        struct sockaddr_in serveraddr,clientaddr;
        char buf[MAXLINE];
        char ipstr[INET_ADDRSTRLEN];//16个字节
        socklen_t clientlen;
    
        //构造用于UDP通信的套接字
        sockfd=socket(AF_INET,SOCK_DGRAM,0);
    
        bzero(&serveraddr,sizeof(serveraddr));
        serveraddr.sin_family =AF_INET;
        serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
        serveraddr.sin_port=htons(SERVER_PORT);
        //printf("%x
    ",INADDR_ANY);
        bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
        while(1){    
            clientlen=sizeof(clientaddr);
            len=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&clientaddr,&clientlen);
    
            printf("client IP %s	 PORT %d
    ",
                    inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
                    ntohs(clientaddr.sin_port));
            i=0;
            while(i<len){
                buf[i]=toupper(buf[i]);
                i++;
            }
            buf[i]='';
            sendto(sockfd,buf,len,0,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
        }
        close(sockfd);
        return 0;
    }

    client.c

    #include <netinet/in.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <ctype.h>
    #define SERVER_PORT 5556 
    #define MAXLINE 4096
    
    int main(int argc,char *argv[])
    {
        struct sockaddr_in serveraddr;
        int confd;
        ssize_t len;
        char ipstr[]="123.206.59.138";
        char buf[MAXLINE];
        if(argc <2){
            printf("./client str
    ");
            exit(1);
        }
        //1.创建一个socket
        confd=socket(AF_INET,SOCK_DGRAM,0);
        //2.初始化服务器地址
        bzero(&serveraddr,sizeof(serveraddr));
        serveraddr.sin_family=AF_INET;
        //
        inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);
        serveraddr.sin_port =htons(SERVER_PORT);
        //3向务器发送数据
        sendto(confd,argv[1],strlen(argv[1]),0,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    
        len=recvfrom(confd,buf,sizeof(buf),0,NULL,0);
        write(STDIN_FILENO,buf,len);
        close(confd);
        return 0;
    }    
  • 相关阅读:
    在浏览器地址栏按回车、F5、Ctrl+F5刷新网页的区别
    RESTful 的总结
    Mvc项目部署IIS报错:没有为请求的URL配置默认文档,并且没有在服务器设置目录浏览
    Ajax的请求方式几传参的区别
    响应式布局中的CSS相对量
    理解 ES6 语法中 yield* 关键字的作用
    理解 ES6 语法中 yield 关键字的返回值
    配置IIS Express以便通过IP地址访问调试的网站
    在IntelliJ IDEA 13中配置OpenCV的Java开发环境
    iOS UITableView获取cell的indexPath及cell内部按钮点击事件处理
  • 原文地址:https://www.cnblogs.com/tla001/p/6603027.html
Copyright © 2020-2023  润新知