• 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;
    }
    
    
  • 相关阅读:
    用纹理贴图模拟反射,NeHe23课球面映射相关
    VS2010: CommandLine Warning D9025
    【转】C RunTime Library 暨 深入理解编译选项的含义 01
    让Doxygen输出中文注释不乱码
    windows环境下memcache配置
    C#中英文字符长度截取
    apache 的工作原理
    pear包安装phpunit
    使用 libevent 和 libev 提高网络应用性能
    PHP发明人谈MVC和网站设计架构——貌似他不支持php用mvc
  • 原文地址:https://www.cnblogs.com/Jimmy1988/p/7895213.html
Copyright © 2020-2023  润新知