inet_pton和inetntop函数。字母p和n代表presentation和numeric。地址的表达presentation格式通常是ASCIL串,数值(numeric)格式则是存在于套接字地址结构中的二进制值。

inet_pton和inet_ntop函数是比较新的函数,它们能够处理ipv4和ipv6的地址转换。

1. inet_pton

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

将src所指的网络地址字符串(如"192.168.0.1")转换成网络使用的二进制数(unsigned long),存放在dst所指的in_addr结构中。使用基本与inet_aton一致,不同的是多了一个参数af(地址族:AF_INET或AF_INET6,分别对应ipv4和ipv6,对应的地址结构为sockaddr_in和sockaddr_in6)。

使用:

sockaddr_in server_addr;
inet_pton(AF_INET, "192.168.0.1", (void *)&server_addr.sin_addr);

2. inet_ntop

const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);

将src所指的网络二进制数转换成网络地址字符串(如"192.168.0.1"),存放在dst所指字符串和返回值中。相比inet_pton多了个参数cnt,定义缓存区dst的大小,防止溢出。如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。

使用:

sockaddr_in server_addr;
char IPdotdec[20]; //存放点分十进制IP地址
inet_pton(AF_INET, "192.168.0.1", (void *)&server_addr.sin_addr);
printf(inet_ntop(AF_INET, (void *)&server_addr.sin_addr, IPdotdec, 16)); // 输出"192.168.0.1"
printf("%s", IPdotdec); // 输出"192.168.0.1"
 readn、writen和readline函数
     字节流套接字上的read和write函数所表现的行为不同于通常的文件I/O.字节流套接字上调用read或write输入或输出的字节数可能比请求的数量少,然而这不是出错状态.这个现象的原因在于内核中用于套接字的缓冲区可能已经达到了极限.此时所需的是调用者再次调用read个write函数,以输入或输出剩余的字节. 我们提供的以下三个函数是每当我们读或写一个字节流套接字时要使用的函数.
//从一个描述符读取n个字节
ssize_t readn(int fd, void* vptr, size_t n)
{
size_t nleft = n; //记录还剩下多少字节数没读取
ssize_t nread; //记录已经读取的字节数
char* ptr = vptr; //指向要读取数据的指针
while(nleft > ) //还有数据要读取
{
if(nread = read(fd,ptr,nleft) < )
if(erron == EINTR)//系统被一个捕获的信号中断
nread = ; //再次读取
else
return -; //返回
else if(nread == )//没有出错但是也没有读取到数据
break; //再次读取
nleft -= nread; //计算剩下未读取的字节数
ptr += nread; //移动指针到以读取数据的下一个位置
}
return (n-nleft); //返回读取的字节数
}
/**************************************************************************************************/
//从一个描述符读文本行,一次一个字节
ssize_t readline(int fd, void* vptr, size_t maxlen)//一个字节一个字节地读取
{
ssize_t rc; //每次读取的字符
ssize_t n; //读取的次数也即读取字符串的长度
char c; //
char* ptr = vptr; //指向要读取的数据的指针
for(n = ;n < maxlen; n++)
{
again:
if((rc = read(fd,&c,)) == )
{
*ptr++ = c; //移动指针
if(c == '\n') //换行符
break; //跳出循环
else if(rc == ) //结束
*ptr = ; //字符串以0结尾
return (n-); //返回读取的字节数 末尾的0不算
}
else
{
if(erron == EINTR)
goto again; //重新读取
return (-)
}
}
*ptr=;
return n;
}
/**************************************************************************************************/
//往一个描述符写n个字节
ssize_t writen(ind fd, const void* vptr, size_t n)
{
size_t nleft = n; //还需要写入的字节数
ssize_t nwritten; //每次写入的字节数
const char* ptr = vptr; //指向要写入的数据的指针
while(nleft > )
{
if((nwritten = write(fd,ptr,nleft)) <= )
{
if(nwritten < && erron == EINTR)
nwritten = ;
else return -;
}
nleft -= nwritten; //计算还需要写入的字节数
ptr += nwritten; //移动数据指针
}
return n;
}

值-结果参数
      一个套接字函数传递一个套接字地址结构时候,该结构总以引用形式来传递,也就是说传递的指向该结构的一个指针,该结构的长度也作为一个参数来传递,不过其传递方式取决于该结构的传递方向:是从进程到内核,还是从内核到进程.如下图所示:

(1)、从进程到内核传递套接字结构函数:bind、connect和sendto.这些函数的一个参数是指向某个套接字地址结构的指针,另一个参数是该结构体的整数大小.例如:

  1. struct sockaddr_in serv; //定义一个套接字地址结构体变量
  2. connect (sockfd, (struct sockaddr *) &serv, sizeof(serv));

(2)、从内核到进程传递套接字地址结构的函数:accept、recvfrom、getsockname和getpeername.这4个函数的其中两个参数指向某个套接字结构体的指针和指向表示该结构体大小的整数变量的指针.例如

  1. struct sockaddr_un cli;                          //定义一个套接字地址结构体变量
  2. socklen_t len = sizeof(cli);                     //该结构体的大小
  3. getpeername(unixfd,(struct sockaddr *)&cli,&len);//len可能会被改变
      把套接字地址结构大小这个参数从一个整数改为指向某个整数变量的指针,其原因在于:当函数被调用时,结构大小是一个值,他告诉内核该结构的大小,这样内核在写该结构时不至于越界;当函数返回时,结构大小又是一个结果,它告诉进程内核在该结构中究竟存储了多少信息.
 

fork和exec函数:

fork最困难的部分是它调用一次却返回两次。在调用进程(称为父进程),它返回一次,返回值是新派生进程(称为子进程)的进程ID,在子进程中它还返回一次,返回0。可通过返回值来判断当前进程是子进程还是父进程。

fork在子进程返回0而不是父进程ID,原因是:子进程只有一个父进程,他总可以调用getppid来得到;而父进程有许多子进程,他没有办法得到各子进程ID。如果父进程想跟踪所有子进程ID,他必须记住fork的返回值。

getsockname与getpeername

是返回套接口关联的本地协议地址和远程协议地址。

int getsockname(int sockfd, struct sockaddr * localaddr, socken_t * addrlen);

int getpeername(int sockfd, struct sockaddr * peeraddr, socken_t * addrlen);

返回0表示成功,返回1表示出错

参数sockfd表示你要获取的套接口的描述字。

localaddr返回本地协议地址描述结构, peeraddr返回远程协议地址描述结构,addrlen分别是上述2个结构的长度。

注意,2个函数的最后一个参数是值-结果参数。

需要这两个函数的理由如下:

  • 在一个没有调用bind的TCP客户上,connect成功返回后,getsockname用于返回由内核赋予该连接的本地IP地址和本地端口号。

  • 在以端口号为0调用bind(告知内核去选择本地临时端口号)后,getsockname用于返回由内核赋予的本地端口号。

  • 在一个以通配IP地址调用bind的TCP服务器上,与某个客户的连接一旦建立(accept成功返回),getsockname就可以用于返回由内核赋予该连接的本地IP地址。在这样的调用中,套接字描述符参数必须是已连接套接字的描述符,而不是监听套接字的描述符。

  • 当一个服务器是由调用过accept的某个进程通过调用exec执行程序时,它能够获取客户身份的唯一途径便是调用getpeername。

  • 例如下面的,inetd调用accept(左上方的方框)返回两个值:已连接套接字描述符connfd,这是函数的返回值;客户的IP地址及端口号,如图中标有“对端地址”的小方框所示(代表一个网际网套接字地址结构)。inetd随后调用fork,派生出inetd的一个子进程。这样父进程的那个套接字地址结构在子进程也可用,那个已连接套接字描述符也是如此。然而当子进程调用exec执行真正的服务器程序(譬如说Telent服务器程序)时,子进程的内存映像被替换成新的Telnet服务器的程序文件(也就是说包含对端地址的那个套接字地址结构就此丢弃),不过那个已连接套接字描述符跨exec继续保持开放。Telnet服务器首先调用的函数之一便getpeername
    ,用于获取客户的IP地址和端口号。
  • 显然,最后一个例子中的Telnet服务器必须在启动之后获取connfd的值。获取该值有两个常用方法:

    • 调用exec的进程可以把这个描述符格式化成一个字符串,再把它作为一个命令行参数传递给新程序。

    • 约定在调用exec之前,总是把某个特定描述符置为所接受的已连接套接字的描述符。

    inetd采用的是第二种方法,它总是把描述符0、1、2置为所接受的已连接套接字的描述符(即将已连接套接字描述符dup到描述符0、1、2,然后close原连接套接字)。

    服务器的代码:

    #include    "unp.h"  
    
    int
    main(int argc, char ** argv)
    {
    int listenfd,connfd;
    struct sockaddr_in servaddr;
    pid_t pid;
    char temp[20]; listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(10010);
    Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));
    Listen(listenfd, LISTENQ);
    for( ; ; )
    {
    struct sockaddr_in local;
    connfd = Accept(listenfd, (SA *)NULL, NULL);
    if((pid = fork()) == 0)
    {
    Close(listenfd);struct sockaddr_in serv, guest;
    char serv_ip[20];
    char guest_ip[20];
    socklen_t serv_len = sizeof(serv);
    socklen_t guest_len = sizeof(guest);
    getsockname(connfd, (struct sockaddr *)&serv, &serv_len);
    getpeername(connfd, (struct sockaddr *)&guest, &guest_len);
    Inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip));
    Inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip));
    printf("host %s:%d guest %s:%dn", serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port));
    char buf[] = "hello world";
    Write(connfd, buf, strlen(buf));
    Close(connfd);
    exit(0);
    }
    Close(connfd);
    }
    }

    客户端的代码:

    #include "unp.h"
    #define DEST_IP "127.0.0.1" int
    main(int argc, char ** argv)
    {
    int sockfd, n;
    char buf[100];
    char serv_ip[20], guest_ip[20];
    struct sockaddr_in servaddr; sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(10010); Inet_pton(AF_INET, DEST_IP, &servaddr.sin_addr);
    Connect(sockfd, (SA *)&servaddr, sizeof(servaddr)); struct sockaddr_in serv, guest;
    socklen_t serv_len = sizeof(serv);
    socklen_t guest_len = sizeof(guest); getsockname(sockfd, (SA *)&guest, &guest_len);
    getpeername(sockfd, (SA *)&serv, &serv_len); Inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip));
    Inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip)); printf("host %s:%d, guest %s:%dn", serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port)); n = Read(sockfd, buf, 100);
    buf[n] = '�';
    printf("%sn", buf);
    Close(sockfd);
    exit(0);
    }

    TCP

    对于服务器来说,在bind以后就可以调用getsockname来获取本地地址和端口,虽然这没有什么太多的意义。getpeername只有在链接建立以后才调用,否则不能正确获得对方地址和端口,所以他的参数描述字一般是链接描述字而非监听套接口描述字。

    对于客户端来说,在调用socket时候内核还不会分配IP和端口,此时调用getsockname不会获得正确的端口和地址(当然链接没建立更不可能调用getpeername),当然如果调用了bind 以后可以使用getsockname。想要正确的到对方地址(一般客户端不需要这个功能),则必须在链接建立以后,同样链接建立以后,此时客户端地址和端口就已经被指定,此时是调用getsockname的时机。

http://blog.chinaunix.net/zt/1016/unixjian_1016285.shtml

http://blog.csdn.net/yirancpp/article/details/8446879

http://tech.ddvip.com/2013-07/1374769636199661.html

《unix网络编程》笔记的更多相关文章

  1. HTML+CSS笔记 CSS笔记集合

    HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...

  2. CSS笔记--选择器

    CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...

  3. HTML+CSS笔记 CSS中级 一些小技巧

    水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...

  4. HTML+CSS笔记 CSS中级 颜色&长度值

    颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...

  5. HTML+CSS笔记 CSS中级 缩写入门

    盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...

  6. HTML+CSS笔记 CSS进阶再续

    CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...

  7. HTML+CSS笔记 CSS进阶续集

    元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...

  8. HTML+CSS笔记 CSS进阶

    文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...

  9. HTML+CSS笔记 CSS入门续集

    继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...

  10. HTML+CSS笔记 CSS入门

    简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...

随机推荐

  1. 用 SQL 计算时间差值

    ;WITH res1 AS ( SELECT * FROM ( SELECT ROW_NUMBER() OVER (PARTITION BY F2 ORDER BY F1) AS rn,F1,F2 F ...

  2. mvc Routing特性优化

    在mvc中,Url地址是利用routing特性来支持,但是这个Routing有个问题,多个不同的地址和指向同一个action方法, 例如: http://test.com (默认) http://te ...

  3. Extjs事件继承注意事项

    Extjs事件继承总结: 在基类中只需配置通用事件,无需配置通用界面,通用界面无效,通用事件一直有效 基表格控制器

  4. Find Minimum in Rotated Sorted Array问题的困惑

    今天做了两题,第二题没解出来,发现太麻烦了,放弃了……明天脑子清楚的时候再做. 第一题就是标题中的这个问题.在一个旋转排序数组中找出最小的值. 针对该问题出了两道不同要求的题目,分别是不考虑重复元素的 ...

  5. addLoadEvent函数

    首先是addLoadEvent函数的代码清单: function addLoadEvent(func){    var oldonload=window.onload;    if(typeof wi ...

  6. HDU 3127 WHUgirls(DP 完全背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3127 题目大意:将一块长x宽y的矩形布料,剪成小的矩形(每个给定的小矩形都对应一个价值),使得所有小矩 ...

  7. C++中string类型对象和double型变量之间的互相转换

    //convert string type value to double type value string s = "23"; double d; istringstream ...

  8. mysql 5.7.16多源复制

    演示一下在MySQL下搭建多主一从的过程. 实验环境: 192.168.24.129:3306 192.168.24.129:3307 192.168.24.129:3308 主库操作 导出数据 分别 ...

  9. python 自动化之路 day 02

    本节内容: 列表.元组操作 字符串操作 1. 列表.元组操作 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 定义列表 1 names = ['Alex',&qu ...

  10. svn-主副分支使用

    主改bug 副加功能, :主合并到副(在副中切换主分支),副调试成功,合并回主(在主切换回副分支) 奇葩的实现了需求 主改bug 副加功能, :主合并到副(在副中切换主分支),副调试成功,合并回主(在 ...