unix网络编程基础接口

typedef unsigned short int uint16_t;

typedef unsigned int uint32_t;

 

typedef unsigned short int sa_family_t;

typedef uint16_t in_port_t;

typedef uint32_t in_addr_t;

 

通用套接字地址结构sockaddr,定义在<sys/socket.h>

sockaddr结构体的缺陷:sa_data把目标地址和端口信息混在一起了

struct sockaddr

{

      sa_family_t  sa_family;

      char sa_data[14];

}

sizeof(structsockaddr) ==16

 

sockaddr_type:type指定了所需的地址类型。

IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>中,sockaddr_in结构体:

struct  sockaddr_in
{
      sa_family_t        sin_family;         /* AF_INET */
      in_port_t          sin_port;              /* 16-bit TCP or UDP port number  */
                                  /* networkbyte ordered */
      struct in_addr     sin_addr;        /* 32-bit IPv4 address */
                                  /* networkbyte ordered */
      unsigned char      sin_zero[sizeof(structsockad dr) –
                                 (sizeof(unsigned short int)) –
                                  sizeof(in_port_t) -
                                  sizeof(struct in_addr)];
}

strcut    in_addr{  
    in_addr_t s_addr;
}

in_addr_t 一般为 32位的unsigned int,其字节顺序为网络顺序(network byte ordered),即该无符号整数采用大端字节序

sizeof(struct  sockaddr_in) ==16== sizeof(struct sockaddr)

其实从sockaddr_in的定义中就看的出来两者大小相同,只不过sockaddr_in只是为了方便使用而已,本来struct就是用来改变一块内存的外在样子,以另一种形式访问。

sin_port和sin_addr必须是NBO(Network Byte Order)。

      然而,大部分函数必须处理来自所支持的任何协议族的套接字地址结构,不仅仅是IPv4的sockaddr_in,在如何声明所传递指针的数据类型上村在一个问题。有了ANSI C后解决办法很简单:void *通用指针类型。然而套接字函数实在ANSI C之前定义的,在1982年采取的办法是定义并使用通用套接字sockaddr。这就要求对这些函数的调用都必须对结构体进行强制类型转换(casting),变成指向通用套接字地址结构的指针,例如:

      struct sockaddr_in  serv;    /* IPv4 socket address structure */

      /* fill in serv{} */

      bind(sockfd, (struct  sockaddr *)&serv,  sizeof(serv));

      其实,如果使用了void *的版本,程序猿会更加方便,让内核可以自己根据传入的sa_family确定真实类型,程序猿就不用手动强制类型转换了。

      sin_addr字段是一个结构,而不仅仅是一个in_addr_t的类型的无符号长整形,这是有历史原因的。早期的版本(4.2BSD)把in_addr结构定义为多种结构的联合(union),允许访问一个32位的IPv4地址中的所有4个字节,或者访问它的2个16位值。这用在地址被划分成A、B和C三类的时期,便于获取地址中的适当字节。然而随着子网划分技术的来临和无类型地址编排的出现,各种地址类正在消失,那个联合已不再需要了。如今大多数系统已经废除了该联合,转而把in_addr定义为仅有一个in_addr_t字段的结构体。

 

 

结构体赋值

NBO(Network ByteOrder)

HBO(Host ByteOrder)

赋值时,常用的一些转化函数:

int inet_aton(const char *cp, structin_addr *inp);

将IPv4字符串点数格式地址cp转化成二进制,存储在inp所指结构体中(NBO),地址有效,返回非0,否则返回0.   strcut in_addr{  in_addr_ts_addr;  }


char *inet_ntoa(struct in_addr in);

将NBO地址转化成字符串点数格式,返回的string存储在静态区域,随后的每次调用将覆盖之前的string。这意味着该函数是不可重入的。另外,需要主要,参数是结构体而不是一个指向结构体的指针。函数以结构体为参数是罕见的,更常见的是以指向结构的指针参数。


in_addr_t inet_addr(const char *cp);

将IPv4字符串点数格式(IPv4numbers-and-dots notation)的地址cp转化成in_addr_t(unsigned  int)   (NBO)

如果输入无效,返回INADDR_NONE(通常是-1),使用这个函数是存在一定问题的,因为-1的二进制对目前大部分路由器上是个有效的地址(255.255.255.255),历史遗留问题,尽量用其它能指示错误的函数替代,像inet_aton,inet_pton,getaddrinfo()


in_addr_t inet_network(const char*cp);

将IPv4字符串点数格式(IPv4numbers-and-dots notation)的地址cp转化成in_addr_t(unsigned  int)   (HBO),返回的字节序不同之外,与in_addr_tinet_addr(const char *cp);相同,返回值也是有问题。

 

下面的两个函数是随IPv6出现的新函数,对于IPv4和IPv6都适用。函数名中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构中的二进制。

int inet_pton(int af, const char*src, void *dst);

将IPv4或IPv6的src指定的string地址,按af指定的类型转化成二进制位,然后拷贝到dst指定的内存中。af只能是AF_INET、AF_INET6,分别指明IPv4和IPv6。使用不支持的af时返回-1,并将errno置为EAFNOSUPPORT,src地址在af类型中无效的,则返回0,成功返回1。比如inet_pton(AF_INET,  “192.168.0.50”,  &sockaddr_in.sin_addr) <= 0

const char *inet_ntop(int af, constvoid *src, char *dst,socklen_t size);

功能与上面的inet_pton相反,转换后存到dst指定的buff,size为buff长度。socklen_t为unsigned int类型,如果size太小,不足以容纳表达式格式结果(包括结尾的空字符),那么返回一个空指针,并置errno为ENOSPC。

 

#include  <netinet/in.h>
uint16_t  htons(uint16_t)           “Host to Network Short”
uint32_t   htonl(uint32_t)          “Host to Network Long”
uint16_t   ntohs(uint16_t)          “Network to Host Short”
uint32_t   ntohl(uint32_t )         “Network to Host Long”

s代表short,l代表long。short和long这个称谓是出自4.2BSD的Digital VAX实现的历史产物。如今我们应该把s视为16位的值(例如TCP或UDP端口号),把l视为一个32位的值(例如IPv4地址)。事实上即使在64位的Digital Alpha中,尽管长整数占用64位,htonl和ntohl函数操作的仍然是32位值。

端口号类型简单,为内置类型,用htons()、ntohs()刚好可以用来转化端口号。

 

一个赋值例子

struct   sockaddr_in  server_addr;
 
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family= AF_INET;     //IPv4
server_addr.sin_port= htons(13);        //13端口号
if(inet_pton(AF_INET, “192.168.0.50”,  &server_addr.sin_addr)  <= 0)
{  fprintf(stderr, “%s\n”,  strerror(errno));  exit(1); }//“192.168.0.50”


另外需要注意bzero使用strings.h,strings.h曾近是POSIX的标准的一部分,但是在POSIX.1-2001标准里面,这些函数被标记为遗留函数而不推荐使用。在POSIX.1-2008标准中,已经没有这些函数了。推荐使用memset代替bzero。


最简单基本的socket编程模型:

TCP server伪代码:

ss = socket()                                                        //create server socket
ss.bind()                                                              //bind socket to address
ss.listen()                                                            //listen for connection
inf_loop:                                                              //server infinite loop
    cs = ss.accept()                                             //accept  client connection
    comm_loop:                                                  //communication loop
        cs.recv()/cs.send()                                   //dialog(receive/send)
    cs.close()                                                      //close client socket
ss.close                                                            //close  server  socket

TCP client伪代码:

cs = socket()                                                                 //create client socket
cs.connect()                                                                 //attempt server connection
comm_loop:                                                                //communication loop
    cs.send()/cs.recv()                                                 //dialog(send/receive)
cs.close()                                                                     //close client socket





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值