套接口结构

IPv4套接口地址结构:

1

2

3

4

5

6

7

struct sockaddr_in{/*16字节*/

uint8_t sin_len;  /*结构体长度,8位*/

sa_family_t sin_family;/*一般来说为AF_INET或PF_INET,8位*/

ln_port_t sin_port;/*必须要采用网络数据格式,16位*/

struct in_addr sin_addr;/*网络字节序,32位*/

unsigned char sin_zero[8];/*8字节为了跟SOCKADDR结构在内存中对齐*/

};

1

2

3

4

struct in_addr

{

in_addr_t s_addr;

}

进程到内核传递套接口地址结构的4个套接口函数:bind,connect,sendto,sendmsg,源自Berkeley的sockargs函数从这四个函数获取地址,并以传入的长度设置sin_len成员。

内核到进程传递套接口地址的5个套接口函数:accetp,recvfrom,recvmsg,getpeername和getsockname,在返回前设置sin_len。

几乎所有实现都增加sin_zero成员,,sin_zero成员暂时不使用,但总是将它置为0。

IPv4地址和TCP、UDP端口号,在套接口地址结构中以网络字节序来存储。

通用套接字地址结构

1

2

3

4

5

6

struct sockaddr

{

uint8_t sin_len;

sa_family_t sa_famliy;

char sa_data[14];   /*14字的协议地址*/

}

套接口函数被定义为指向通用套接口地址的指针,对于这些函数的调用,必须将特定协议的套接口地址结构的指针类型转换为指向通用套接口地址的指针,例如:

1

2

struct sockaddr_in serv;

bind(fd, (struct sockaddr *)&serv, sizeof(serv)

IPv6套接口地址结构:

1

2

3

4

5

6

7

struct sockaddr_in6{/*24字节*/

uint8_t sin6_len;  /*结构体长度,8位*/

sa_family_t sin6_family;/*一般来说为AF_INET或PF_INET,8位*/

in_port_t sin6_port;/*必须要采用网络数据格式,16位*/

uint32_t sin6_flowinfo;/*流标,32位,24位流量标号+4位优先级+4位保留*/

struct in6_addr sin6_addr;/*网络字节序,128位*/

};

1

2

3

4

struct in6_addr

{

in_addr_t s6_addr[16]; /*128位*/

}

从进程到内核的函数我们仍然用bind举例,bind将指向套接口地址结构的指针和结构长度传给内核,这样内核就知道从进程拷贝多少数据,但是这只是一个最大值,实例可能并没有拷贝那么多数据,具体拷贝了多少存档在len的指针里,而从内核到进程,传递的是套接口地址结构的指针和表示地址结构大小的指针,该指针指向的长度就是实际写入的长度。因为当函数被调用时结构大小是一个值,当函数返回时,结构大小是一个结果。当使用值-结果参数作为套接口地址结构的长度时,若套接口地址结构是定长的,则从内核返回也是定长的,否则,返回值可能比结构的最大长度小。

大端-小端

将低序字节存在起始地址为小端,将高序字节存在起始地址为大端。比如:数字16的16进制表示为0x0010,数字4096的16进制表示为0x1000。由于Intel机器是小尾端,存储数字16时实际顺序为1000,存储4096时实际顺序为0010。我们可以通过一小段程序来判断大端还是小端:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <stdio.h>

# 通过存储位置来确定字节序

int main(int argc, char **argv){

union{

short s;

char c[sizeof(short)];

}un;

un.s = 0x0102;

if(sizeof(short) == 2){

if(un.c[0] == 1 && un.c[1] == 2)

printf("big-endian\n");

else if(un.c[0] == 2 && un.c[1] == 1)

printf("little-endian\n");

else

printf("UNKNOW\n");

}else

printf("sizeof(short) = %d", sizeof(short));

return 0;

}

而网际协议在处理多节整数时使用大端字节序,因为存在不一致,我们就需要考虑主机字节序和网络字节序的转换转换用如下四个函数,h代表host,n代表net,s代表short,l代表long:

1

2

3

4

5

6

7

#include <netinet/in.h>

uint16_t htons(uint16_t host16bitvalue);

uint32_t htonl(uint32_t host32bitvalue);

uint16_t ntohs(uint16_t net16bitvalue);

uint32_t ntohl(uint32_t net32bitvalue);

协议无关的地址转换函数

以下两个函数协议无关,IPv4和IPv6均可处理,字母p和n分别代表presentation和numeric。

1

2

3

4

5

#include <arpa/inet.h>

int inet_pton(int family, const char *strptr, void *addrptr)

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len)

下面是一个只支持IPv4的简单inet_pton:

1

2

3

4

5

6

7

8

9

10

11

1213

14

15

16

17

18

19

20

21

22

23

24

25

26

27

#include <arpa/inet.h>

#include <sys/socket.h>

#include <string.h>

#include <errno.h>

#include <stdio.h>

int main(int argc, char **argv){

struct sockaddr_in  servaddr;

int inets_pton_t(int, const char *, void *);

servaddr.sin_family = AF_INET;

servaddr.sin_port   = htons(2329);

inet_pton_t(AF_INET, argv[1], &servaddr.sin_addr);

printf("addr is %d\r\n", servaddr.sin_addr);

return 0;

}

int inet_pton_t(int family, const char *strptr, void *addrptr){

if(family == AF_INET){

struct in_addr in_val;

if(inet_aton(strptr, &in_val)){

memcpy(addrptr, &in_val, sizeof(struct in_addr));

return 1;

}

return 0;

}

errno = EAFNOSUPPORT;

return -1;

}

下面是一个只支持IPv4的简单的inet_ntop:

1

2

3

4

5

6

7

8

9

10

11

1213

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#include <stdio.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#include <string.h>

#include <errno.h>

#ifndef INET_ADDRSTRLEN

#define INET_ADDRSTRLEN 16

#endif

int main(int argc, char **argv){

const char *inet_ntop_t(int, const void *, char *, size_t);

struct sockaddr_in  servaddr;

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(2329);

servaddr.sin_addr.s_addr = 606284554;

char c[INET_ADDRSTRLEN];

const char *str_ptr;

str_ptr = inet_ntop_t(AF_INET, &servaddr.sin_addr, c, sizeof(c));

printf("str_ptr is %s\r\n", str_ptr);

printf("addr is %s\r\n", c);

return 0;

}

const char *inet_ntop_t(int family, const void *addrptr, char *strptr, size_t len){

const u_char *p = (const u_char *)addrptr;

if(family == AF_INET){

char temp[INET_ADDRSTRLEN];

snprintf(temp, sizeof(temp), "%d, %d, %d, %d", p[0], p[1], p[2], p[3]);

if(strlen(temp) >= len){

errno = ENOSPC;

return NULL;

}

strcpy(strptr, temp);

return (strptr);

}

errno = EAFNOSUPPORT;

return NULL;

}

readn_t, writen_t, readline_t

这里定义了几个函数,是对read,write的封装,是为了预防返回不足的字节计数值,比如write的时候内核套接口缓存区已经满了,只写入部分,那我们仍然要继续写入,直到写完,读也是一样。

下面给出以上三个函数代码,这些代码都是跑过的,可直接拷贝使用:

readn_t:

1

2

3

4

5

6

7

8

9

10

11

1213

14

15

16

17

18

19

20

21

22

#include "lib_sock.h"

ssize_t readn_t(int fd, void *vptr, size_t n){

ssize_t nleft;

ssize_t nread;

char *ptr;

ptr = vptr;

nleft = n;

while(nleft > 0){

if((nread = read(fd, ptr, nleft)) < 0){ # 读取n个数据

if(errno == EINTR)

nread = 0; # 信号中断,nleft不变,重新读取

else

return -1; # 否则报异常

}else if(nread == 0) # 为0则已无数据,直接结束

break;

nleft -= nread;

ptr += nread;

} # 循环结束,则数据读取完成

return (n - nleft); # 返回实际读取的size。

}

writen_t:

1

2

3

4

5

6

7

8

9

10

11

1213

14

15

16

17

18

19

20

21

22

#include "lib_sock.h"

ssize_t writen_t(int fd, const void *vptr, size_t n)

{

size_t n_left;

ssize_t nwriten;

const char *ptr;

ptr = vptr;

n_left = n;

while(n_left > 0){ # 一直写,直到所有数据全部写入

if((nwriten = write(fd, ptr, n_left)) <= 0){

if(errno == EINTR)

nwriten = 0;

else

return -1;

}

n_left -= nwriten;

ptr += nwriten;

}

return n;

}

readline_t:

1

2

3

4

5

6

7

8

9

10

11

1213

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

#include "lib_sock.h"

static ssize_t my_read(int, char *);

ssize_t readline_t(int fd, void *vptr, size_t maxlen){

ssize_t n, rc;

char c, *ptr;

ptr = vptr;

for(n=1; n<maxlen; n++){

again:

if((rc = my_read(fd, &c)) == 1){ # 每次读取一个数值

*ptr++ = c;

if(c == '\n') # 遇到换行符,则一行读取完成,break

break;

}else if(rc == 0){# 未读取到信息

if(n == 1) # 数据为空

return 0;

else    # 数据读取完成

break;

}else{

if(errno == EINTR)

goto again;

return -1;

}

}

*ptr = 0;

return n;

}

static ssize_t my_read(int fd, char *ptr){

static int read_cnt = 0;

static char *read_ptr;

static char read_buf[MAXLINE];

if(read_cnt <= 0){

again:

if((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0){ #数据读到缓存

if(errno == EINTR)

goto again;  #中断继续读取

return -1;

}else if(read_cnt == 0)

return 0;

read_ptr = read_buf;

}

read_cnt--;

*ptr = *read_ptr++; # 每次取一个数据

return 1;

}

测试套接字是否为套接口描述字的函数isfdtype

1

2

3

4

5

6

7

8

9

10

11

#include "../lib/lib_sock.h"

int isdftype(int fd, int fdtype){

struct stat buf;

if(fstat(fd, &buf) < 0)

return -1;

if((buf.st_mode & S_IFMT) == fdtype)

return 1;

else

return 0;

}

UNIX网络编程总结三的更多相关文章

  1. 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

    本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...

  2. 【unix网络编程第三版】阅读笔记(三):基本套接字编程

    unp第三章主要介绍了基本套接字编程函数.主要有:socket(),bind(),connect(),accept(),listen()等. 本博文也直接进入正题,对这几个函数进行剖析和讲解. 1. ...

  3. 【UNIX网络编程第三版】阅读笔记(一):代码环境搭建

    粗略的阅读过<TCP/IP详解>和<计算机网络(第五版)>后,开始啃这本<UNIX网络编程卷一:套接字联网API>,目前linux下的编程不算太了解,在阅读的过程中 ...

  4. 【unix网络编程第三版】ubuntu端口占用问题

    <unix网络编程>一书中的代码并不是能直接运行,有时候需要结合各方面的知识来解决,大家在这本书的时候,一定要把代码都跑通,不难你会错过很多学习的机会! 1.问题描述 本人在阅读<U ...

  5. 【unix网络编程第三版】阅读笔记(二):套接字编程简介

    unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...

  6. 【UNIX网络编程(三)】TCP客户/server程序演示样例

    上一节给出了TCP网络编程的函数.这一节使用那些基本函数编写一个完毕的TCP客户/server程序演示样例. 该样例运行的过程例如以下: 1.客户从标准输入读入一行文本,并写给server. 2.se ...

  7. 【unix网络编程第三版】阅读笔记(四):TCP客户/服务器实例

    本篇博客主要记录一个完整的TCP客户/服务器实例的编写,以及从这个实例中引发的对僵死进程的处理等问题. 1. TCP客户/服务器功能需求 本实例完成以下功能: (1) 客户从标准输入读入一行文本,并写 ...

  8. unix网络编程第三版源代码ubuntu下配置的问题解决

    第一步:首先下载本书配套的源码unpv13e.tar.gz 第二步:解压后进入根文件夹有一个README 4 Execute the following from the src/ directory ...

  9. Unix网络编程第三版源码编译

    配置: $ cd Unix-Network-Programming/ $ chmod 755 configure $ ./configure 主要的工作是检查系统是否有源码编译所依赖的各种资源(系统版 ...

随机推荐

  1. Android and HTML5 开发手机应用(转载)

    作为一个WEB开发者,HTML5让我兴奋,因为它可以将桌面应用程序功能带入浏览器中.但在国内,看着到处横行的IE8版本以下的浏览器,觉得到能大规模使用HTML5技术的那天,还遥遥无期.但面对iOS及A ...

  2. Planting Trees

    Planting Trees 给定N*N矩阵,求子矩形满足里面最大元素最小元素之差不超过M 单调队列 枚举上边界,下边界,及右边界, 用两个单调队列,一个维护最大值,一个维护最小 求左边界 #incl ...

  3. D. White Lines

    D. White Lines 给定一个$n\times n$的$WB$矩阵,给定一个$k*k$的能把$B$变成$W$的橡皮擦,求橡皮擦作用一次后,全为$W$的行.列总数最大值 前缀和差分 #inclu ...

  4. js 在输出到页面的5中方式

    1.alert("要输出的内容"); ->在浏览器中弹出一个对话框,然后把要输出的内容展示出来 ->alert都是把要输出的内容首先转换为字符串然后在输出的 2.doc ...

  5. ES5 Object.defineProperties / Object.defineProperty 的使用

    临时笔记,稍后整理 var obj = { v: , render: function () { console.log(") } }; // Object.defineProperties ...

  6. java操作JSON字符串转换成对象的时候如何可以不建立实体类也能获取数据

    引入依赖 <dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson& ...

  7. 只有一个form 的程序, onactivate 只触发一次。

    https://blog.csdn.net/saint13/article/details/454615 Form的onActivate事件 2005年08月15日 01:08:00 阅读数:3406 ...

  8. [转载]借助openssl解析ECC公钥

    void GetPubKey(const char* FilePath, char* PubKey) { unsigned ]; unsigned char *pTmp = NULL; FILE *f ...

  9. Python工具库(感谢backlion整理)

    漏洞及渗透练习平台: WebGoat漏洞练习平台: https://github.com/WebGoat/WebGoat webgoat-legacy漏洞练习平台: https://github.co ...

  10. K近邻实现

    1 定义画图函数,用来可视化数据分布 (注:jupyternotebook来编写的代码) import matplotlib.pyplot as plt import numpy as np %con ...