zoukankan      html  css  js  c++  java
  • Socket编程

    基础知识部分:http://www.cnblogs.com/Jimmy1988/p/7839940.html

    1. 基本流程

    mark

    Process Client Server Comment
    socket() 创建socket文件描述符 同← 通信双方都需建立
    socket文件描述符是应用层通信接口
    bind() 可选
    一般不绑定,由内核自动分配
    必选
    IP地址不能为公网地址
    远程:一般设置为"0.0.0.0"或 INADDR_ANY
    局域网:可设置为内网地址
    详细可见:
    https://www.cnblogs.com/ok-lanyan/articles/2634242.html
    Listen() 可选,一般不监听 必选
    使socket处于监听网络状态
    阻塞等待
    connect() 向服务端发起链接请求 包含目标主机的IP和Port
    accept() 服务端接受链接请求
    接受链接后,使用一个新的socket描述符,
    使服务端可接受多个链接
    可以提取客户端的IP和Port
    Send/Recv() 进行数据包的发送和接受 同← socket同样可以认为是文件,
    可以直接read/write()
    close() 关闭socket链接 同← 可以分开关闭读/写操作

    2. API

    1). Socket()

    #include <sys/socket.h>
    
    int socket(int domain, int type, int protocol)
    
    /* Return Value: 
     * If successed: A file descriptor for new socket
     * else : -1
     */
    

    ①. domain:

    用于选择本次通信的协议簇(地址簇);
    其详细定义见 /usr/include/bits/socket.h, 常用的宏如下:

    Name Purpose Man page
    AF_UNIX,
    AF_LOCAL
    Local communication unix(7)
    AF_INET IPv4 Internet protocols ip(7)
    AF_INET6 IPv6 Internet protocols ipv6(7)
    AF_IPX IPX - Novell protocols
    AF_NETLINK Kernel user interface device netlink(7)

    ②. type

    协议类型
    详细信息见/usr/include/bits/socket_types.h,常用的如下:

    Type Purpose
    SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams.
    An out-of-band data transmission mechanism may be supported.
    SOCK_DGRAM Supports datagrams (connectionless,
    unreliable messages of a fixed maximum length).

    ③. protocol

    一般由系统自动选择,通常设置为0

    2). bind()

    当使用socket()创建一个socket后,存在着一个name space,但是没有内存地址与其关联;
    本函数的目的就是为了分配内存地址给socket

    #include <sys/socket.h>
    
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    
    /* Return Value:
     * If successsed: 0 
     * else: -1
     */
    

    ①. socktfd

    socket()函数返回的socket文件描述符

    ②. addr

    分配给socket的地址空间首地址;
    根据协议的不同,结构体定义不同,可以使用man 7 ip/ipv6/etc..查看;
    一般的定义形式如下:

    /* Structure describing a generic socket address.  */
    struct sockaddr
      {
        __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
        char sa_data[14];       /* Address data.  */
      };
    
    /*IF flags = AF_INET, 即IPv4*/
    struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */
                   in_port_t      sin_port;   /* port in network byte order */
                   struct in_addr sin_addr;   /* internet address */
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };
    
    

    ③. addrlen

    地址长度,一般使用sizeof(xx)

    3). Listen()

    #include <sys/socket.h>
    
    int listen(int sockfd, int backlog)
    
    /*
     * backlog: 请求排队的最大长度
     */
     
    /* Return Value
     * If successed: 0
     * else: -1
     */
    

    4). connect()

    #include <sys/socket.h>
    
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
    /* Return Value
     * If successed: 0
     * else: -1
     */
    

    5). accept()

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    /* Return Value
     * If successed: 有效的接收到的socket描述符
     * else: -1
     */
    

    6). read/write()

    读写时,可以把socket看成普通文件,因此读写的方式与普通文件相同:

    #include <unistd.h>
    
    ssize_t read(int fd, void *buf, size_t count);
    ssize_t write(int fd, const void *buf, size_t count);
    
    

    7). send/recv()

    针对面向连接的socket的读写操作

    #include <sys/socket.h>
    
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
    /* Arguments: 
     * 1. buf: 数据存放位置
     * 2. len: 数据发送/接收大小
     * 3. flags: 见下面
     */
     
    /* Return Value
     * If successed: 发送/接收的数据量
     * else: -1
     */ 
    

    **flags: **

    enum
      {
        MSG_OOB     = 0x01, /* Process out-of-band data.  */
    #define MSG_OOB     MSG_OOB
        MSG_PEEK        = 0x02, /* Peek at incoming messages.  */
    #define MSG_PEEK    MSG_PEEK
        MSG_DONTROUTE   = 0x04, /* Don't use local routing.  */
    #define MSG_DONTROUTE   MSG_DONTROUTE
    #ifdef __USE_GNU
        /* DECnet uses a different name.  */
        MSG_TRYHARD     = MSG_DONTROUTE,
    # define MSG_TRYHARD    MSG_DONTROUTE
    #endif
        MSG_CTRUNC      = 0x08, /* Control data lost before delivery.  */
    #define MSG_CTRUNC  MSG_CTRUNC
        MSG_PROXY       = 0x10, /* Supply or ask second address.  */
    #define MSG_PROXY   MSG_PROXY
        MSG_TRUNC       = 0x20,
    #define MSG_TRUNC   MSG_TRUNC
        MSG_DONTWAIT    = 0x40, /* Nonblocking IO.  */
    #define MSG_DONTWAIT    MSG_DONTWAIT
        MSG_EOR     = 0x80, /* End of record.  */
    #define MSG_EOR     MSG_EOR
        MSG_WAITALL     = 0x100, /* Wait for a full request.  */
    #define MSG_WAITALL MSG_WAITALL
        MSG_FIN     = 0x200,
    #define MSG_FIN     MSG_FIN
        MSG_SYN     = 0x400,
    #define MSG_SYN     MSG_SYN
        MSG_CONFIRM     = 0x800, /* Confirm path validity.  */
    #define MSG_CONFIRM MSG_CONFIRM
        MSG_RST     = 0x1000,
    #define MSG_RST     MSG_RST
        MSG_ERRQUEUE    = 0x2000, /* Fetch message from error queue.  */
    #define MSG_ERRQUEUE    MSG_ERRQUEUE
        MSG_NOSIGNAL    = 0x4000, /* Do not generate SIGPIPE.  */
    #define MSG_NOSIGNAL    MSG_NOSIGNAL
        MSG_MORE        = 0x8000,  /* Sender will send more.  */
    #define MSG_MORE    MSG_MORE
        MSG_WAITFORONE  = 0x10000, /* Wait for at least one packet to return.*/
    #define MSG_WAITFORONE  MSG_WAITFORONE
    
        MSG_CMSG_CLOEXEC    = 0x40000000    /* Set close_on_exit for file
                           descriptor received through
                           SCM_RIGHTS.  */
    #define MSG_CMSG_CLOEXEC MSG_CMSG_CLOEXEC
      };
    
    

    8). close/shutdown()

    两种方式关闭socket

    I. close()

    完全关闭socket通道,包括读和写

    #include <unistd.h>
    
    int close(int fd);
    

    II. shutdown()

    具备更强的灵活性,可选择性关闭读或写

     #include <sys/socket.h>
    
    int shutdown(int sockfd, int how);
    

    how:

    • how=0: 只关闭读通道;
    • how=1: 只关闭写通道
    • how=2: 关闭读写通道

    9). getsockname/getpeername()

    I. 获取socket的本地地址

    要求:已完成绑定本地IP

    #include <sys/socket.h>
    
    int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    /* Return Value
     * If successed: 0
     * else: -1
     */ 
    

    II. 获取socket远程信息

    要求:该socket已经完成远程连接

    #include <sys/socket.h>
    
    int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    /* Return Value
     * If successed: 0
     * else: -1
     */ 
    

    10). 获取本机IP地址等信息

    #include <ifaddrs.h>
    
    int getifaddrs(struct ifaddrs **ifap);
    void freeifaddrs(struct ifaddrs *ifa)
    
    /*Return value:
     * If successed:0 
     * else: -1
     */
     
     struct ifaddrs {
        struct ifaddrs  *ifa_next;    /* Next item in list */
        char            *ifa_name;    /* Name of interface */
        unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */
        struct sockaddr *ifa_addr;    /* Address of interface */
        struct sockaddr *ifa_netmask; /* Netmask of interface */
        union {
               struct sockaddr *ifu_broadaddr; /* Broadcast address of interface */
                struct sockaddr *ifu_dstaddr;
                /* Point-to-point destination address */
                } ifa_ifu;
        #define              ifa_broadaddr ifa_ifu.ifu_broadaddr
        #define              ifa_dstaddr   ifa_ifu.ifu_dstaddr
        void            *ifa_data;    /* Address-specific data */
        };
    
    

    3. 示例代码

    本次实现client/server两端的实时聊天功能,彼此不影响,可随时发送/接收对方数据;
    当输入"quit”后,结束本次链接。

    1. Client.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <errno.h>
    
    #define SIZE 1024
    #define PORT 12345
    //#define IP_ADDR "192.168.139.129"
    
    #define IP_ADDR "139.196.121.132"
    //#define IP_ADDR "47.89.246.154"
    
    int sockfd;
    
    void *snd(void)
    {
        int len = 0;
        char buf[SIZE];
        while(1)
        {
            memset(buf,'', sizeof(buf));
            printf("
    Input msg send to server:  ");
            fgets(buf, SIZE, stdin);
            if(buf[0] != '')
            {
                len = send(sockfd, buf, sizeof(buf), 0);
                if(len < 0)
                {
                    printf("SND: Some Error occured or disconnection!
    ");
                    break;
                }                   
    
                if(!strncasecmp(buf, "quit",4))
                {
                    printf("SND: sub thread has quit!
    ");
                    break;
                }
                printf("
    Send msg: %s 
    ", buf);
            }
        }
        
        pthread_exit(NULL);
    }
    
    void *rcv(void)
    {
        int len=0;
        char buf[SIZE];
            
        while(1)
        {
            memset(buf,'', sizeof(buf));
    
            len = recv(sockfd, buf, SIZE, 0);
    
            if(len < 0)
            {
                printf("RCV: Some error occured!
    ");
                break;
            }
            
            if(!strncasecmp(buf, "quit",4))
            {
                printf("RCV: sub thread has quit!
    ");
                break;
            }
    
            printf("
    The received msg:%s
    ", buf);
        }
    
        pthread_exit(NULL);
    }
    
    int main(int argc, char *argv[])
    {
        int ret;
    
        //1. create socket
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(-1 == sockfd)
        {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        //2. bind the ip addr
        struct sockaddr_in dest_addr;
        memset(&dest_addr, 0, sizeof(struct sockaddr_in));
        
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(PORT);
        dest_addr.sin_addr.s_addr = inet_addr(IP_ADDR);
    
    /*
        ret = bind(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in));
        if(-1 == ret)
        {
            perror("bind");
            exit(EXIT_FAILURE);
        }
     */   
    
        //3. request the connection    
        ret = connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if(-1 == ret)
        {
            perror("connect");
            exit(EXIT_FAILURE);
        }
        printf("Connect the server(IP:%s) successed!
    ", IP_ADDR);
    
    
        //5. send/receive the message
        pthread_t tid1, tid2;
    
        if( 0 != pthread_create(&tid1, NULL, (void *)*snd, NULL))
        {
            perror("pthread_create");
            exit(errno);
        }
    
        if( 0 != pthread_create(&tid2, NULL, (void *)*rcv, NULL))
        {
            perror("pthread_create");
            exit(errno);
        }
    
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
    
        return 0;
    }
    
    

    2. Server.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    
    
    #define SIZE 1024
    #define PORT 12345
    //#define IP_ADDR "192.168.139.129"
    #define IP_ADDR "139.196.121.132"
    
    int main(int argc, char *argv[])
    {
        int ret;
        pthread_t tid;
        int sockfd;
    
        //1. create socket
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(-1 == sockfd)
        {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        //2. bind the ip addr
        struct sockaddr_in local_addr;
        memset(&local_addr, 0, sizeof(struct sockaddr_in));
        
        local_addr.sin_family = AF_INET;
        local_addr.sin_port = htons(PORT);
        local_addr.sin_addr.s_addr = inet_addr(IP_ADDR);
    
        ret = bind(sockfd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in));
        if(-1 == ret)
        {
            perror("bind");
            exit(EXIT_FAILURE);
        }
        
        //3. listen the connection    
        unsigned int lisnum = 5;
        ret = listen(sockfd, lisnum);
        if(-1 == ret)
        {
            perror("listen");
            exit(EXIT_FAILURE);
        }
        printf("Wait for client connection!
    ");
    
        //4. accept the connection
        int new_fd;
        socklen_t len;
        struct sockaddr_in client_addr;
        
        new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &len);
        if(-1 == new_fd)
        {
            perror("accept");
            exit(EXIT_FAILURE);
        }
        
        else
        {
    //        printf("Server got connection: 
    	Address:%s	Port:%d	Socket=%d
    ", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), new_fd);
        }
    
        //5. send/receive the message
        char buf[SIZE];
        ssize_t length;    
        pid_t pid;
        pid = fork();
        if(pid == -1)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }    
        
        if(pid == 0)
        {
            while(1)
            {
                memset(buf, '', SIZE);
                printf("Pls input the msg  to send: ");
                fgets(buf, SIZE, stdin);
                
                if((length=strlen(buf)) < 0)
                {
                    printf("send error, the length=%d
    ", length);
                    break;
                }
    
                length = send(new_fd, buf, sizeof(buf), 0);            
    
                if(!strncasecmp(buf, "quit", 4))
                {
                    printf("Snd: close the connection!
    ");
                    break;
                }
            }
        }
    
        else
        {
            while(1)
            {
                memset(buf, '', SIZE);
                length = recv(new_fd, buf, SIZE, 0);        
    
                if(length >= 0)
                {
                    printf("
    Receive msg:  %s
    ", buf);
                }
    
                else 
                {
                    printf("Error occured
    ");
                    break;
                }
    
                if(!strncasecmp(buf, "quit", 4))
                {
                    printf("Recv: close the connection!
    ");
                    break;
                }
            }
            
        }
        
        //Close the socket 
        close(sockfd);
        close(new_fd);
        
        return 0;
    }
    
    
  • 相关阅读:
    OPPO R9sPlus MIFlash线刷TWRP Recovery ROOT详细教程
    OPPO R11 R11plus系列 解锁BootLoader ROOT Xposed 你的手机你做主
    努比亚(nubia) M2青春版 NX573J 解锁BootLoader 并进入临时recovery ROOT
    华为 荣耀 等手机解锁BootLoader
    青橙 M4 解锁BootLoader 并刷入recovery ROOT
    程序员修炼之道阅读笔03
    冲刺8
    典型用户模板分析
    学习进度八
    冲刺7
  • 原文地址:https://www.cnblogs.com/Jimmy1988/p/7895213.html
Copyright © 2011-2022 走看看