• socket编程中最常用的几个数据类型和转换函数


    高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换。

    第一个结构类型是:struct sockaddr 该类型是用来保存socket信息的 

    struct sockaddr { 
       unsigned short sa_family;
        /* 地址族, AF_xxx */ 
       char sa_data[14];       /* 14 字节的协议地址 */ 
    }; 
    sa_family一般为AF_INET; 
    sa_data则包含该socket的IP地址和端口号。 
    另外还有一种结构类型: 
    struct sockaddr_in 
       short int sin_family;      /* 地址族 */ 
       unsigned short int sin_port;    /* 端口号 */ 
       struct in_addr sin_addr;    /* IP地址 */ 
       unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小*/ 
      };

    这个结构使用更为方便。sin_zero(它用来将sockaddr_in结构填充到与struct sockaddr同样的长度)应该用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。sin_family通常被赋AF_INET;sin_port和sin_addr应该转换成为网络字节优先顺序

    我们下面讨论几个字节顺序转换函数: 

    htons() -- "Host to Network Short" 
    htonl() -- "Host to Network Long" 
    ntohs() -- "Network to Host Short" 
    ntohl() -- "Network to Host Long" 

    在这里, h表示"host" ,n表示"network",s 表示"short",l表示 "long"。 
    打开socket 描述符、建立绑定并建立连接 
    socket函数原型为: 
    int socket(int domain, int type, int protocol); domain参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM;protocol通常赋值"0"。Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。 

    一旦通过socket调用返回一个socket描述符,你应该将该socket与你本机上的一个端口相关联(往往当你在设计服务器端程序时需要调用该函数。随后你就可以在该端口监听服务请求;而客户端一般无须调用该函数)。 
    Bind函数原型为: 
    int bind(int sockfd,struct sockaddr *my_addr, int addrlen); Sockfd是一个socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。 
    最后,对于bind 函数要说明的一点是,你可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号: 

    my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */ 
    my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */ 

    通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。Bind()函数在成功被调用时返回0;遇到错误时返回"-1"并将errno置为相应的错误号。另外要注意的是,当调用函数时,一般不要将端口号置为小于1024的值,因为1~1024是保留端口号,你可以使用大于1024中任何一个没有被占用的端口号。 

    Connect()函数用来与远端服务器建立一个TCP连接,其函数原型为: 
    int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); Sockfd是目的服务器的sockt描述符;serv_addr是包含目的机IP地址和端口号的指针。
    遇到错误时返回-1,并且errno中包含相应的错误码。进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,内核会自动选择一个未被占用的端口供客户端来使用 

    Listen()——监听是否有服务请求 
    在服务器端程序中,当socket与某一端口捆绑以后,就需要监听该端口,以便对到达的服务请求加以处理。 
    int listen(int sockfd, int backlog); Sockfd是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,
    进入的连接请求将在队列中等待accept()它们(参考下文)。Backlog对队列中等待服务的请求的数目进行了限制,
    大多数系统缺省值为20。当listen遇到错误时返回-1,errno被置为相应的错误码。 

    故服务器端程序通常按下列顺序进行函数调用: 
    socket(); 
    bind(); 
    listen(); 
    /* accept() goes here */ 

    accept()——连接端口的服务请求。 
    当某个客户端试图与服务器监听的端口连接时,该连接请求将排队等待服务器accept()它。通过调用accept()函数为其建立一个连接,accept()函数将返回一个新的socket描述符,来供这个新连接来使用。而服务器可以继续在以前的那个socket上监听,同时可以在新的socket描述符上进行数据send()(发送)和recv()(接收)操作: 
    int accept(int sockfd, void *addr, int *addrlen); sockfd是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值。 

    send()和recv()——数据传输 这两个函数是用于面向连接的socket上进行数据传输。 
    send()函数原型为: 
    int send(int sockfd, const void *msg, int len, int flags); Sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针。 Len是以字节为单位的数据的长度。flags一般情况下置为0 
    char *msg = "Beej was here!"; 
    int len,bytes_sent; ... ... 
    len = strlen(msg); 
    bytes_sent = send(sockfd, msg,len,0); ... ... 

    Send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。所以需要对send()的返回值进行测量。当send()返回值与len不匹配时,应该对这种情况进行处理。 

    recv()函数原型为: 
    int recv(int sockfd,void *buf,int len,unsigned int flags); Sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。 

    sendto()和recvfrom()——利用数据报方式进行数据传输 
    在无连接的数据报socket方式下,由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址

    sendto()函数原型为: 
    int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen); 该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。
    Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。 

    recvfrom()函数原型为: 
    int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen); from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。
    当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。 

    应注意的一点是,当你对于数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。 
    close()和shutdown()——结束数据传输 
    当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:close(sockfd); 
    你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。 

    int shutdown(int sockfd,int how); 
    Sockfd的含义是显而易见的,而参数 how可以设为下列值: 
    ·0-------不允许继续接收数据 
    ·1-------不允许继续发送数据 
    ·2-------不允许继续发送和接收数据,均为允许则调用close () 

    shutdown在操作成功时返回0,在出现错误时返回-1(并置相应errno)。 

    DNS——域名服务相关函数 
    由于IP地址难以记忆和读写,所以为了读写记忆方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。
    函数gethostbyname()就是完成这种转换的,函数原型为: 
    struct hostent *gethostbyname(const char *name); 函数返回一种名为hosten的结构类型,它的定义如下: 
    struct hostent { 
       char *h_name; /* 主机的官方域名 */    
       char **h_aliases; /* 一个以NULL结尾的主机别名数组 */    
       int h_addrtype; /* 返回的地址类型,在Internet环境下为AF-INET */               

    int h_length; /*地址的字节长度 */ 
       char **h_addr_list; /* 一个以0结尾的数组,包含该主机的所有地址*/   
    }; 
     

    #define h_addr h_addr_list[0]
     /*在h-addr-list中的第一个地址*/

  • 相关阅读:
    服务部署 RPC vs RESTful
    模拟浏览器之从 Selenium 到splinter
    windows程序设计 vs2012 新建win32项目
    ubuntu python 安装numpy,scipy.pandas.....
    vmvare 将主机的文件复制到虚拟机系统中 安装WMware tools
    ubuntu 修改root密码
    python 定义类 简单使用
    python 定义函数 两个文件调用函数
    python 定义函数 调用函数
    python windows 安装gensim
  • 原文地址:https://www.cnblogs.com/hnrainll/p/2116624.html
Copyright © 2020-2023  润新知