套接口,也叫“套接字”。是操做系统内核中的一个数据结构,它是网络中的节点进行相互通讯的门户。网络通讯,归根到底仍是进程间的通讯(不一样计算机上的进程间通讯)。在网络中,每个节点(计算机或路由)都有一个网络地址,也就是IP地址。两个进程通讯时,首先要肯定各自所在的网络节点的网络地址。可是,网络地址只能肯定进程所在的计算机,而一台计算机上极可能同时运行着多个进程,因此仅凭网络地址还不能肯定究竟是和网络中的哪个进程进行通讯,所以套接口中还须要包括其余的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系。因此,使用端口号和网络地址的组合能够惟一的肯定整个网络中的一个网络进程。 例如,如网络中某一台计算机的IP为123.207.251.21,操做系统分配给计算机中某一应用程序进程的端口号为1500,则此时 123.207.251.21:1500就构成了一个套接口。linux
在网络技术中,端口大体有两种意思:编程
端口号的范围从0-65535,一类是由互联网指派名字和号码公司ICANN负责分配给一些经常使用的应用程序固定使用的“周知的端口”,其值通常为0~1024,例如http的端口号是80,ftp为21,ssh为22,telnet为23等;还有一类是用户本身定义的,一般是大于1024的整型值。bash
一般用户在表达IP地址时采用的是点分十进制表示的数值(或者是为冒号分开的十六进制Ipv6地址),而在一般使用的socket编程中使用的则是二进制值,这就须要将这两个数值进行转换。 ipv4地址:32bit, 4字节,至关于一个整型,一般采用点分十进制记法。 例如对于:10000000 00001011 00000011 00011111, 点分十进制表示为:128.11.3.31。网络
Linux中的网络编程是经过socket接口来进行的。socket是一种特殊的I/O接口,它也是一种文件描述符。它是一种经常使用的进程之间通讯机制,经过它不只能实现本地机器上的进程之间的通讯,并且经过网络可以在不一样机器上的进程之间进行通讯。数据结构
每个socket都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket也有一个相似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的链接创建、数据传输等操做都是经过socket来实现的。ssh
用于TCP通讯,流式套接字提供可靠的、面向链接的通讯流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。socket
用于UDP通讯,数据报套接字定义了一种无链接的服务,数据经过相互独立的报文进行传输,是无序的,而且不保证是可靠、无差错的。它使用数据报协议UDP。函数
用于新的网络协议实现的测试等,原始套接字容许对底层协议如IP或ICMP进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发。测试
//头文件<netinet/in.h> sockaddr和sockaddr_in大小一致
struct sockaddr {
unsigned short sa_family; /*地址族*/
char sa_data[14]; /*14字节的协议地址,包含该socket的IP地址和端口号。*/
};
struct sockaddr_in {
short int sa_family; /*地址族 AF_INET IPv4协议 AF_INET6 IPv6协议*/
unsigned short int sin_port; /*端口号*/
struct in_addr sin_addr; /*IP地址*/
unsigned char sin_zero[8]; /*填充0 以保持与struct sockaddr一样大小*/
};
struct in_addr {
unsigned long int s_addr; /* 32位IPv4地址,网络字节序 */
};
复制代码
计算机数据存储有两种字节优先顺序:高位字节优先(称为大端模式)和低位字节优先(称为小端模式)。内存的低地址存储数据的低字节,高地址存储数据的高字节的方式叫小端模式。内存的高地址存储数据的低字节,低地址存储数据高字节的方式称为大端模式。 eg,对于内存中存放的数0x12345678来讲:ui
若是称某个系统所采用的字节序为主机字节序,则它多是小端模式的,也多是大端模式的。而端口号和IP地址都是以网络字节序存储的,不是主机字节序,网络字节序都是大端模式。要把主机字节序和网络字节序相互对应起来,须要对这两个字节存储优先顺序进行相互转化。这里用到四个函数:htons(),ntohs(),htonl()和ntohl().这四个地址分别实现网络字节序和主机字节序的转化,这里的h表明host,n表明network,s表明short,l表明long。一般16位的IP端口号用s表明,而IP地址用l来表明。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); //将主机的无符号长整型数转换成网络字节序
uint16_t htons(uint16_t hostshort); //将主机的无符号短整形数转换成网络字节序
uint32_t ntohl(uint32_t netlong); //将一个无符号长整型数从网络字节序转换为主机字节序
uint16_t ntohs(uint16_t netshort); //将一个无符号短整形数从网络字节序转换为主机字节序
复制代码
一般用户在表达地址时采用的是点分十进制表示的数值(或者是为冒号分开的十进制Ipv6地址),而在一般使用的socket编程中使用的则是32位的网络字节序的二进制值,这就须要将这两个数值进行转换。这里在Ipv4中用到的函数有inet_aton()、inet_addr()和inet_ntoa(),而IPV4和Ipv6兼容的函数有inet_pton()和inet_ntop()。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *straddr, struct in_addr *addrptr);
char *inet_ntoa(struct in_addr inaddr);
in_addr_t inet_addr(const char *straddr);
复制代码
函数inet_aton():将点分十进制数的IP地址转换成为网络字节序的32位二进制数值。返回值:成功,则返回1,不成功返回0. 参数straddr:存放输入的点分十进制数IP地址字符串。 参数addrptr:传出参数,保存网络字节序的32位二进制数值。 函数inet_ntoa():将网络字节序的32位二进制数值转换为点分十进制的IP地址。 函数inet_addr():功能与inet_aton相同,可是结果传递的方式不一样。inet_addr()若成功则返回32位二进制的网络字节序地址。
#include <arpa/inet.h>
int inet_pton(int family, const char *src, void *dst);
const char *inet_ntop(int family, const void *src, char *dst, socklen_t len);
复制代码
函数inet_pton跟inet_aton实现的功能相似,只是多了family参数,该参数指定为AF_INET,表示是IPv4协议,若是是AF_INET6,表示IPv6协议。 函数inet_ntop跟inet_ntoa相似,其中len表示表示转换以后的长度(字符串的长度)。
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main() {
char ip[] = "192.168.0.101";
struct in_addr myaddr;
memset((void*)&myaddr, 0, sizeof(struct in_addr));
/* inet_aton */
int iRet = inet_aton(ip, &myaddr);
if ( iRet == 1)
{
printf("%ld\n", myaddr.s_addr);
/* inet_addr */
printf("%x\n", inet_addr(ip));
}
else
{
printf("call inet_aton failed\n");
}
/* inet_pton */
iRet = inet_pton(AF_INET, ip, &myaddr);
if ( iRet == 1 )
{
printf("%x\n", myaddr.s_addr);
}
else
{
printf("call inet pton failed\n");
}
myaddr.s_addr = 0xac100ac4;
/* inet_ntoa */
printf("%s\n", inet_ntoa(myaddr));
/* inet_ntop */
inet_ntop(AF_INET, &myaddr, ip, 16);
puts(ip);
return 0;
}
复制代码
一般,人们在使用过程当中都不肯意记忆冗长的IP地址,尤为到Ipv6时,地址长度多达128位,那时就更加不可能一次性记忆那么长的IP地址了。所以,使用主机名或域名将会是很好的选择。主机名与域名的区别:主机名一般在局域网里面使用,经过/etc/hosts文件,主机名能够解析到对应的ip,域名一般是再internet上使用。
众所周知,百度的域名为:www.baidu.com,而这个域名其实对应了一个百度公司的IP地址,那么百度公司的IP地址是多少呢?咱们能够利用ping www.baidu.com来获得百度公司的ip地址。那么,系统是如何将www.baidu.com 这个域名转化为IP地址220.181.111.148的呢?
在linux中,有一些函数能够实现主机名和地址的转化,最多见的有gethostbyname()、gethostbyaddr()等,它们均可以实现IPv4和IPv6的地址和主机名之间的转化。其中gethostbyname()是将主机名转化为IP地址,gethostbyaddr()则是逆操做,是将IP地址转化为主机名。
函数原型:
#include <netdb.h>
struct hostent* gethostbyname(const char* hostname);
struct hostent* gethostbyaddr(const char* addr, size_t len, int family);
结构体:
struct hostent {
char *h_name; /*正式主机名*/
char **h_aliases; /*主机别名*/
int h_addrtype; /*主机IP地址类型 IPv4为AF_INET*/
int h_length; /*主机IP地址字节长度,对于IPv4是4字节,即32位*/
char **h_addr_list; /*主机的IP地址列表*/
}
#define h_addr h_addr_list[0] /*保存的是ip地址*/
复制代码
Example:
//test.cpp 将百度的www.baidu.com 转换为ip地址
#include <netdb.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char **argv) {
char *ptr, **pptr;
struct hostent *hptr = NULL;
char str[32] = {0};
if ( argc < 2 )
{
printf("please input an addr,eg:./a.out www.baidu.com\n");
return 0;
}
/* 取得命令后第一个参数,即要解析的域名或主机名 */
ptr = argv[1];
/* 调用gethostbyname()。结果存在hptr结构中 */
if((hptr = gethostbyname(ptr)) == NULL)
{
printf("gethostbyname error for host:%s\n", ptr);
return 0;
}
else
{
/* 将主机的规范名打出来 */
printf("official hostname:%s\n", hptr->h_name);
}
/* 主机可能有多个别名,将全部别名分别打出来 */
for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)
printf("alias:%s\n", *pptr);
/* 根据地址类型,将地址打出来 */
switch(hptr->h_addrtype)
{
case AF_INET:
case AF_INET6:
pptr = hptr->h_addr_list;
/* 将刚才获得的全部地址都打出来。其中调用了inet_ntop()函数 */
for(; *pptr!=NULL; pptr++ )
{
printf("address:%s\n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
}
printf("first address: %s\n", inet_ntop(hptr->h_addrtype, hptr->h_addr, str, sizeof(str)));
break;
default:
printf("unknown address type\n");
break;
}
return 0;
}
复制代码
编译运行 g++ test.cpp ./a.out www.baidu.com
official hostname:www.a.shifen.com
alias:www.baidu.com
address:14.215.177.39
address:14.215.177.38
first address: 14.215.177.39
复制代码
原文首发自简书:www.jianshu.com/p/2df8cdada…