• Linux Socket


    1. API

    #include <sys/types.h>
    #include <sys/socket.h>
    
    
    /************************************************************************
     * 常用socket结构体
     ***********************************************************************/
    
    // 通用函数类型:
    struct sockaddr{
      sa_family_t  sa_family;
      char         sa_data[14];
    }
    
    //如ipv4对应的是:
    struct sockaddr_in {
        sa_family_t    sin_family; /* address family: AF_INET */
        in_port_t      sin_port;   /* port in network byte order 2字节*/
        struct in_addr sin_addr;   /* internet address 4字节*/
      unsigned char sin_zero[8];
    };
    
    //Internet address
    struct in_addr {
        uint32_t       s_addr;     /* address in network byte order */
    };
    
    //ipv6对应的是: 
    struct sockaddr_in6 { 
        sa_family_t     sin6_family;   /* AF_INET6 */ 
        in_port_t       sin6_port;     /* port number */ 
        uint32_t        sin6_flowinfo; /* IPv6 flow information */ 
        struct in6_addr sin6_addr;     /* IPv6 address */ 
        uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */ 
    };
    struct in6_addr { 
        unsigned char   s6_addr[16];   /* IPv6 address */ 
    };
    
    //Unix域对应的是: 
    #define UNIX_PATH_MAX    108
    struct sockaddr_un { 
        sa_family_t sun_family;               /* AF_UNIX */ 
        char        sun_path[UNIX_PATH_MAX];  /* pathname */ 
    };
    
    /************************************************************************
     * 工具方法
     ***********************************************************************/
    
    //将字符串形式的IP地址转化为整数型的IP地址(网络字节序)
    int_addr_t indet_addr(const char *cp);
    
    //将整数形式的IP地址转化为字符串形式的IP地址
    char *inet_ntoa(struct in_addr);
        
    //网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位。
    //而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。
    //可见多字节数值在发送前,在内存中数值应该以大端法存放。 
    //网络字节序说是大端字节序。
    
    //将32位的数据从主机字节序转换为网络字节序
    uint32_t htonl(uint32_t hostlong);
      
    //将16位的数据从主机字节序转换为网络字节序
    uint16_t htons(uint16_t hostshort);
      
    //将32位的数据从网络字节序转换为主机字节序
    uint32_t ntohl(uint32_t netlong);
      
    //将16位的数据从网络字节序转换为主机字节序
    uint16_t ntohs(uint16_t netshort);
      
    /************************************************************************
     * API
     ***********************************************************************/
    
    /**
     * 创建socket
     *
     * @param domain IPv4直接填写AF_INET
     * @param type TCP使用SOCK_STEAM, UDP使用SOCK_DGRAM
     * @param protocol 填0即可
     * @return 成功返回socket句柄,失败返回-1
     **/
    int socket(int domain,int type,int protocol);
    
    /**
     * 一旦你有了一个套接口以后,下一步就是把套接口绑定到本地计算机的某一个端口上。
     * 但如果你只想使用connect()则无此必要。
     *
     * @param addrlen sizeof(struct sockaddr)
     * @return 失败返回-1
     **/
    int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
    
    /**
     * 绑定端口后就可以开启监听了
     * @param backlog 进入队列中允许的连接的个数。
     * 		进入的连接请求在使用系统调用accept()应答之前要在进入队列中等待。
     * 		这个值是队列中最多可以拥有的请求的个数。大多数系统的缺省设置为20。你可以设置为5或者10。
     * @return 失败返回-1
     **/
    int listen(int sockfd,int backlog);
    
    /**
     * 如果你使用connect()系统调用,那么你不必知道你使用的端口号。
     * 当你调用connect()时,它检查套接口是否已经绑定,如果没有,它将会分配一个空闲的端口。
     *
     * @param addrlen sizeof(struct sockaddr)
     * @return 成功返回0,失败返回-1
     **/
    int connect(int sockfd,struct sockaddr *serv_addr, int addrlen);
    
    /**
     * 服务端阻塞监听客户端连接
     *
     * @return 成功返回连接句柄,出错返回-1
     **/
    int accept(int sockfd,void *addr,int *addrlen);
    
    /**
     * 发送数据,服务端发送接收数据使用accept()返回的句柄
     *
     * @param msg 发送数据的指针
     * @param len 发送数据长度
     * @param flags 设0
     * @return 返回实际发送的数据长度,如果没法送完要接着下次发送
     **/
    int send(int sockfd,const void* msg,int len,int flags);
    
    /**
     * 接收数据,服务端接收接收数据使用accept()返回的句柄
     *
     * @param len 接收最大长度
     * @return 返回实际接收长度,出错返回-1
     **/
    int recv(int sockfd,void* buf,int len,unsigned int flags);
    
    /**
     * 发送数据,除了两个参数以外,其他的参数和系统调用send()时相同。
     *
     * @param to 包含目的IP地址和端口号的数据结构sockaddr的指针
     * @return 同send()
     **/
    int sendto(int sockfd, const void* msg, int len, unsigned int flags,
               const struct sockaddr *to, int tolen);
    
    /**
     * 接收数据,除了两个参数以外,其他的参数和系统调用recv()时相同
     *
     * @param from 包含源IP地址和端口号的数据结构sockaddr的指针
     * @return 同recv()
     **/
    int recvfrom(int sockfd,void* buf, int len, unsigned int flags,
    		     struct sockaddr* from,int* fromlen);
    
    /**
     * 关闭连接
     **/
    int close(int sockfd);
    
    /**
     * 使用系统调用shutdown(),可有更多的控制权。它允许你在某一个方向切断通信,或者切断双方的通信
     * 
     * @param how 0,1,2
     * @return 成功返回0,失败返回-1
     **/
    int shutdown(int sockfd,int how);
    
    /**
     * 返回对方ip
     **/
    int getpeername(int sockfd,struct sockaddr *addr,int *addrlen);
    
    /**
     * 返回当前计算机名字
     * 
     * @return 成功返回0,失败返回-1
     **/
    int gethostname(char *hostname,size_t size);
    

      

    2. 建立连接

    服务端

    服务器端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。

    socket() -> bind() -> listen() -> accept()
    

    客户端

    客户端先初始化socket,然后与服务端连接,服务端监听成功则连接建立完成

    socket() -> connect()
    

    3. 示例

    服务端

    /* File Name: server.c */  
    #include<stdio.h>  
    #include<stdlib.h>  
    #include<cstring>  
    
    #include<errno.h>  
    #include<sys/types.h>  
    #include<sys/socket.h>  
    
    #define DEFAULT_PORT 8000  
    #define MAXLINE 4096  
    int main(int argc, char** argv)  
    {  
        int socket_fd, connect_fd;  
        struct sockaddr_in servaddr;  
        char buff[4096];  
        int  n;  
        
        //初始化Socket  
        if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
        	printf("create socket error: %s(errno: %d)
    ",strerror(errno),errno);  
        	exit(0);  
        }  
        
        //初始化本地地址参数  
        memset(&servaddr, 0, sizeof(servaddr));  
        servaddr.sin_family = AF_INET;  
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  
        servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT  
      
        //将本地地址绑定到所创建的套接字上  
        if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
       	 	printf("bind socket error: %s(errno: %d)
    ",strerror(errno),errno);  
        	exit(0);  
        }  
        //开始监听是否有客户端连接  
        if( listen(socket_fd, 10) == -1){  
        	printf("listen socket error: %s(errno: %d)
    ",strerror(errno),errno);  
        	exit(0);  
        }  
        
        printf("======waiting for client's request======
    ");  
        while(1){  
    		//阻塞直到有客户端连接,不然多浪费CPU资源。  
            if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){  
            	printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
            	continue;  
       		 }  
    		//接受客户端传过来的数据  
        	n = recv(connect_fd, buff, MAXLINE, 0);  
    		//向客户端发送回应数据  
        	if(!fork()){//子进程进入if 
            	if(send(connect_fd, "Hello,you are connected!
    ", 26,0) == -1)  
            		perror("send error");  
            	close(connect_fd);  
            	exit(0);//退出子进程
        	}  
        	buff[n] = '';  
        	printf("recv msg from client: %s
    ", buff);  
        	close(connect_fd);  
        }  
        
        close(socket_fd);  
    }  
    

    客户端

    /* File Name: client.c */  
      
    #include<stdio.h>  
    #include<stdlib.h>  
    #include<string.h>  
    #include<errno.h>  
    #include<sys/types.h>  
    #include<sys/socket.h>  
    #include<netinet/in.h>  
      
    #define MAXLINE 4096  
      
      
    int main(int argc, char** argv)  
    {  
        int    sockfd, n,rec_len;  
        char    recvline[4096], sendline[4096];  
        char    buf[MAXLINE];  
        struct sockaddr_in    servaddr;  
      
      
        if( argc != 2){  
        printf("usage: ./client <ipaddress>
    ");  
        exit(0);  
        }  
      
      
        if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){  
        printf("create socket error: %s(errno: %d)
    ", strerror(errno),errno);  
        exit(0);  
        }  
      
      
        memset(&servaddr, 0, sizeof(servaddr));  
        servaddr.sin_family = AF_INET;  
        servaddr.sin_port = htons(8000);  
        if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){  
        printf("inet_pton error for %s
    ",argv[1]);  
        exit(0);  
        }  
      
      
        if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){  
        printf("connect error: %s(errno: %d)
    ",strerror(errno),errno);  
        exit(0);  
        }  
      
      
        printf("send msg to server: 
    ");  
        fgets(sendline, 4096, stdin);  
        if( send(sockfd, sendline, strlen(sendline), 0) < 0)  
        {  
        printf("send msg error: %s(errno: %d)
    ", strerror(errno), errno);  
        exit(0);  
        }  
        if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {  
           perror("recv error");  
           exit(1);  
        }  
        buf[rec_len]  = '';  
        printf("Received : %s ",buf);  
        close(sockfd);  
        exit(0);  
    } 
    

      

  • 相关阅读:
    十分钟学会Java8:lambda表达式和Stream API
    史上最全的Spring Boot Cache使用与整合
    史上最全的Spring Boot Starter开发手册
    深入理解JAVA虚拟机(内存模型+GC算法+JVM调优)
    深入理解Mybatis技术与原理
    c++入门之命名空间存在的意义
    机器学习第一篇——最近邻kNN
    Python学习第十八篇——低耦合函数设计思想
    Python学习第十六篇——异常处理
    Python学习第十五篇——类继承和类实例化
  • 原文地址:https://www.cnblogs.com/wuyepeng/p/9741773.html
Copyright © 2020-2023  润新知