• Socket编程实践(2) Socket API 与 简单例程


    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程。该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端。

    socket()函数

    socket()函数用于创建一个套接字。这就好像购买了一个电话。不过该电话还没有分配号码。

    
     #include <sys/types.h>         
    
     #include <sys/socket.h>
    
    
    
     int socket(int domain, int type, int protocol)
    
    

    参数说明:

    • domain:指定通信的协议族,这些协议族定义在头文件< sys/socket.h >中。使用IPV4协议族时,该参数设置为AF_INET

    • type :指定socket的类型。在上一篇文章中介绍过,套接字常用的有三种类型:流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW

    • protocol : 该参数指定了一种协议类型用于所选择的套接字。如果仅有一种协议支持某种套接字类型,那么该参数可以定义为0,此时使用默认协议;如果一种套接字类型可能有多种协议类型,那么必须显式指定协议类型。关于具体细节,可以man socket进行查阅。

    socket()的返回值:成功时返回非负整数;失败时返回-1;

    bind() 函数

    bind()函数绑定一个本地地址到套接字上,这相当于为电话绑定了号码。当一个套接字通过socket()被创建,它并没有绑定到具体的地址上,bind()来完成这个步骤。 bind()函数的函数原型如下:

    
     #include <sys/types.h>    
    
     #include <sys/socket.h>
    
    
    
    int bind(int sockfd, const struct sockaddr *addr,
    
            socklen_t addrlen);
    
    

    参数说明:

    • sockfd:socket()函数创建后成功返回的套接字

    • addr : 需要绑定的地址

    • addrlen:套接字的大小

    这里需要使用到sockaddr_in结构来表示一个地址,该结构如下:

    
    struct sockaddr_in
    
    {
    
    sa_family_t  sin_family;
    
    in_port_t    sin_port;
    
    struct in_addr sin_addr;
    
    };
    
    struct in_addr
    
    {
    
    uint32_t s_addr;
    
    }
    
    

    sockaddr_in需要强制转换为struct sockaddr*类型,传递给bind()函数的第二个参数。下面是一段例程:

    
    int main()
    
    {
    
    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    
    if(listenfd == -1)
    
    err_exit("socket error");
    
    struct sockaddr_in addr;
    
    //填充结构
    
    addr.sin_family  = AF_INET;
    
    addr.sin_port= htons(8001); //主机字节序转换为网络字节序
    
    addr.sin_addr= htonl(INADDR_ANY);//绑定主机的任一个IP地址
    
    /*下面两句具有相同的功能:都是绑定到本机ip地址*/
    
    //inet_aton("127.0.0.1",&addr.sin_addr);
    
    //addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    
    
    if(bind(listenfd,(const struct sockaddr*)&addr,sizeof(addr))==-1)
    
    err_exit("bind error");
    
    }
    
    

    listen()函数

    当使用socket()创建了一个套接字时,该套接字默认是主动套接字。使用listen()函数会使套接字称为一个被动套接字,也就是说,该套接字将被用来接受连接的数据,这些数据通过accept()函数接收。

    listen()函数的函数原型如下:

    
     #include <sys/types.h>         
    
     #include <sys/socket.h>
    
     int listen(int sockfd, int backlog);
    
    

    参数说明:

    • sockfd : 套接字。

    • backlog: 指定连接队列的长度。

    对于给定的监听套接字,内核需要维护两个队列:

    1. 已完成连接队列:该队列中的连接处于ESTABLISHED状态,也即是已经完成了三次握手过程。

    2. 未完成连接队列:该队列中的连接处于SYN_RCVD状态,还未建立连接。

    两个队列的长度之和不能够超过backlogi。如果一个连接请求到达时未完成队列已满,客户端可能接收到一个错误指示ECONNREFUSED。服务器使用accept()函数从已完成连接队列的队头返回一个连接。下面是TCP为监听套接口维护的两个队列:

    accept()函数

    accept()函数用于从已完成队列的队头返回一个连接。它的函数原型为:

    
    #include <sys/types.h>          
    
    #include <sys/socket.h>
    
    
    
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    
    
    

    参数说明:

    • sockfd : 服务器套接字

    • addr :用于接收对等方(客户端)的套接字地址。该参数填充为NULL时,不接收任何信息。

    • addrlen:返回对等方的套接字地址长度。如果不关心可以设置为NULL,否则一定要初始化。

    函数返回值:成功返回一个非负整数,代表一个套接字;失败返回-1;

    connect()函数

    该函数用于建立一个连接到指定的套接字。函数的原型为:

    
    #include <sys/types.h>      
    
    #include <sys/socket.h>
    
    int connect(int sockfd, const struct sockaddr *addr,
    
                       socklen_t addrlen);
    
    
    
    

    参数说明:

    • sockfd : 未连接的套接字

    • addr:未连接的套接字地址

    • addrlen:addr的长度

    一个简单的socket 通信例程

    客户端代码:

    
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<arpa/inet.h>
    #include<netinet/in.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    
    #define ERR_EXIT(m)
            do 
            {
                    perror(m);
                    exit(EXIT_FAILURE);
            }while(0)
    
    int main()
    {
            /*创建一个套接字*/
            int sock = socket(AF_INET,SOCK_STREAM,0);
            if(sock == -1)
                  ERR_EXIT("socket");
    
            /*定义一个地址结构*/
            struct sockaddr_in servaddr;
            memset(&servaddr,0,sizeof(servaddr));
            servaddr.sin_family = AF_INET;
            servaddr.sin_port = htons(5888);
            servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
            /*进行连接*/
            if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
            {
                    ERR_EXIT("connect");
            }
            else
            {
                    printf("连接成功
    ");
            }
            char sendbuf[1024]={0};
            char recvbuf[1024]={0};
            /*从标准输入中读入*/
            while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
            {
                    write(sock ,sendbuf,strlen(sendbuf));
                    if(read (sock,recvbuf,sizeof(recvbuf))>0)
                    {
                            printf("从服务器接收信息:
    ");
                            fputs(recvbuf,stdout);
                    }
                    memset(&sendbuf,0,sizeof(sendbuf));
                    memset(&recvbuf,0,sizeof(recvbuf));
            }
            close(sock);
            return 0;
        } 
    
    

    服务器端代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<arpa/inet.h>
    #include<netinet/in.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include<string.h>
    #define ERR_EXIT(m)
            do 
            {
                    perror(m);
                    exit(EXIT_FAILURE);
            }while(0)
    
    int main()
    {
            /* 创建一个套接字*/
    
            int listenfd= socket(AF_INET ,SOCK_STREAM,0);
            if(listenfd==-1)
                    ERR_EXIT("socket");
    
            /*定义一个地址结构并填充*/
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;   //协议族为ipv4
            addr.sin_port = htons(5888); //绑定端口号
            addr.sin_addr.s_addr = htonl(INADDR_ANY);//主机字节序转为网络字节序
    
            /*将套接字绑定到地址上*/
            if(bind(listenfd,(const struct sockaddr *)&addr ,sizeof(addr))==-1)
            {
                    ERR_EXIT("bind");
            }
            /*监听套接字,成为被动套接字*/
            if(listen(listenfd,SOMAXCONN)<0)
            {
                    ERR_EXIT("Listen");
            }
            struct sockaddr_in peeraddr;
            socklen_t peerlen = sizeof(peeraddr);
    
            /*定义一个套接字,通常称为已连接套接字*/
            int conn ;
            conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);
            if(conn <0)
                ERR_EXIT("accept error");
            else
                printf("连接到服务器的客户端的IP地址是:%s,端口号是:%d
    ",inet_ntoa(peeraddr.sin_addr),htons(peeraddr.sin_port));
    
            /*循环获取数据、发送数据*/
            char recvbuf[1024];
            while(1)
            {
                    memset(recvbuf,0,sizeof(recvbuf));
                    int ret = read(conn,recvbuf ,sizeof(recvbuf));
                    fputs(recvbuf,stdout);
                    write(conn,recvbuf,sizeof(recvbuf));
            }
            /*关闭套接字*/
            close(listenfd);
            close(conn);
            return 0;
    }
    
                
    
    
  • 相关阅读:
    qmake复制文件
    py3 execfile实现
    联合索引特殊案例
    pytest html
    问题|安装ts时报错:EACCES: permission denied, access '/usr/local/lib/node_modules’。意为没有权限(安装其他的也报这个错误如:cnpm install g @vue/cli)。
    问题|苹果电脑android studio 同步时,报警告License for package Android SDK BuildTools 29.0.2not accepted
    问题|iframe监听时,菜单栏初始状态为放开时,点击按钮菜单栏可收缩;而菜单栏初始状态为防收缩时,点击按钮菜单栏呈放开状态。
    问题|全局安装liveserver时(npm install g liveserver),安装成功,但启动项目时报错,(****not commond)
    问题|当iview表单进行校验的时候,有数据还进行校验
    问题|当在远程拉取一个分支后(如user20210720),在本地新建一个分支并切换到此分支(如816),修改完代码后想要上传到新建的远程的另一个分支(如dev_local), 此时,在终端直接切换远程的另一个分支会报错: error: pathspec 'dev_local' did not match any file(s) known to git。
  • 原文地址:https://www.cnblogs.com/QG-whz/p/5427976.html
Copyright © 2020-2023  润新知