接触网络编程一年多了,最近在系统的学习vnp两本书,对基础知识做一些总结,希望理解的更透彻清晰,希望能有更多的沉淀。

1.套接口地址

  针对IPv4和IPv6地址族,分别定义了两种类型的套接口地址:sockaddr_in和sockaddr_in6,两种套接口地址结构如下所示: 

  1. /* IPv4地址族套接口地址结构 */
  2. struct in_addr {
  3. in_addr_t s_addr; /* IPv4地址,网络序存储 */
  4. }
  5. struct sockaddr_in {
  6. uint8_t sin_len; /* 结构体大小 */
  7. sa_family_t sin_family; /* 地址族:AF_INET */
  8. in_port_t sin_port; /* 16位端口号,网络序存储 */
  9. struct in_addr sin_addr; /* IPv4地址,网络序存储 */
  10. char sin_zero[] /* 保留字段,未使用 */
  11. };
  12.  
  13. /* IPv6地址族套接口地址结构 */
  14. struct in6_addr {
  15. uint8_t s6_addr[]; /* IPv6地址,网络序存储 */
  16. }
  17. struct sockaddr_in6 {
  18. uint8_t sin6_len; /* 结构体大小 */
  19. sa_family_t sin6_family; /* 地址族:AF_INET */
  20. in_port_t sin6_port; /* 16位端口号,网络序存储 */
  21. uint32_t sin6_flowinfo /* 流标记或优先级,网络序存储 */
  22. struct in6_addr sin6_addr; /* IPv4地址,网络序存储 */
  23. };

这两个套接口地址结构在/netinet/in.h头文件中定义,上面结构体的描述和头文件中的定义有一些差别,比如在头文件中sa_family_t成员通过宏定义在了公共部分,另外表示结构体大小的sin_len和sin6_len成员,在某些实现中没有定义,两种套接口地址结构都定义了地址族类型成员,是自描述的结构。

  另外为了使套接口函数能够统一处理所有协议族的套接口地址结构,定义了通用套接口地址:

  1. /* 通用套接口地址结构 */
  2. struct sockaddr {
  3. uint8_t sa_len; /* 结构体大小 */
  4. sa_family_t fa_family; /* 地址族 */
  5. char sa_data[]; /* 协议地址 */
  6. }

通用套接口地址结构的用处只有两个:1、用于套接口函数的声明,任何使用套接口地址指针为参数的函数,其指针全部声明为指向通用套接口地址类型。2、类型转换,用于将特定于协议族的地址指针转换为通用地址指针。

2.套接口函数

  socket创建套接口描述符:

  1. #include <sys/socket.h>
  2. /* 功能:创建套接口描述符-sockfd。
  3. 参数:1、domain-协议族,常用AF_INET、AF_INET6、AF_UNIX或AF_LOCAL等。
  4. 2、type-套接口类型,常用SOCK_STREAM、SOCK_DGRAM 等。
  5. 3、protocol-协议,一般设置为0,协议有内核根据前两个参数选择。
  6. 返回值:成功则返回非负描述符,失败返回-1。
  7. */
  8. int socket(int domain, int type, int protocol)

  bind将套接口描述符绑定到特定的套接口地址:

  1. #include <sys/socket.h>
  2. #include <sys/socket.h>
  3. /* 功能:将套接口描述符-sockfd绑定到特定的套接口地址。
  4. 参数:1、sockfd-套接口描述符。
  5. 2、addr-指向通用套接口地址的指针。
  6. 3、addrlen-套接口地址结构的大小。
  7. 返回值:成功则返回0,失败返回-1,并修改errno为相应的值。
  8. */
  9. int bind(int sockfd, const struct sockaddr *addr,
  10. socklen_t addrlen);

  connect将本地描述符连接到套接口地址addr指定的服务端:

  1. #include <sys/socket.h>
  2. /* 功能:将本地描述符连接到套接口地址addr指定的服务端。
  3. 参数:1、sockfd-套接口描述符。
  4. 2、addr-指向通用套接口地址的指针。
  5. 3、addrlen-套接口地址结构的大小。
  6. 返回值:成功则返回0,失败返回-1,并修改errno为相应的值。
  7. */
  8. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  listen将套接口描述符设置为被动连接状态,用于在指定端点进行监听:

  1. #include <sys/socket.h>
  2. /* 功能:将套接口描述符设置为被动等待连接状态,用于在指定端点进行监听。
  3. 参数:1、sockfd-套接口描述符。
  4. 2、backlog-未完成连接对了和已完成连接队列的最大值。
  5. 返回值:成功则返回0,失败返回-1,并修改errno为相应的值。
  6. */
  7. int listen(int sockfd, int backlog);

  accept用于服务器端接受客户端的一个连接:

  1. #include <sys/socket.h>
  2. /* 功能:用于服务器端接受客户端的一个连接。
  3. 参数:1、sockfd-套接口描述符。
  4. 2、addr-指向通用套接口地址的指针,函数返回时保存了客户端套接口地址信息。
  5. 3、addrlen-套接口地址结构的大小。
  6. 返回值:成功则返回一个已连接到客户端的套接口描述符,失败返回-1,并修改errno为相应的值。
  7. */
  8. int accept(int sockfd, struct sockaddr *addr,
  9. socklen_t *addrlen);

 3.字节序转换函数和地址转换函数

  1. #include <arpa/inet.h>
  2. /* 功能:主机序转换成网络序。
  3. 参数:1、hostlong/hostshort-待转换32位长整形或者16位短整形。
  4. 返回值:转换成网络序的32位长整形或16为短整形。
  5. */
  6. uint32_t htonl(uint32_t hostlong);
  7. uint16_t htons(uint16_t hostshort);
  8. /* 功能:网络序转换成主机序。
  9. 参数:1、netlong/netshort-待转换32位长整形或者16位短整形。
  10. 返回值:转换成主机序的32位长整形或16为短整形。
  11. */
  12. uint32_t ntohl(uint32_t netlong);
  13. uint16_t ntohs(uint16_t netshort);

备注:多字节类型数据的表示/存储方式分为小端序和大端序。小端序,高位字节存储在高地址内存空间,低位字节存储在低位内存空间,大端序恰恰相反。对于不同架构的主机序不一样,有的架构主机序采用小端字节序,有的架构主机序采用大端架构,另外网络序采用大端字节序当在网络上传输字节流时,不需要考虑字节序的问题,当在网络上传输多字节类型数据时需要考虑字节序问题

验证本地host字节序的方法:

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4. int main(void) {
  5. union {
  6. short a;
  7. char c[sizeof(short)];
  8. } t;
  9. t.a = 0x0102;
  10. /* 高位字节存存储于低地址内存,
  11. * 低位字节序存储于高地址内存 */
  12. if ( == t.c[] && == t.c[])
  13. printf("big-endian\n");
  14. else
  15. printf("little-endian\n");
  16. return ;
  17. }

4.多字节操纵函数

  string.h中定义了两组多字节类型操作函数,这两组函数不对待处理的多字节类型数据做任何假设。第一组函数由b开头,起源于4.2BSD,当前几乎所有支持套接口的系统都提供这一组函数。

  1. /* 功能:将s指向大小为n的内存空间初始化为全0。
  2. 参数:1、s-内存地址。
  3. 2、n-内存大小
  4. 返回值:无。
  5. */
  6. void bzero(void *s, size_t n);
  7. /* 功能:内存拷贝。
  8. 参数:1、src-源内存地址。
  9. 2、dest-目标内存地址。
  10. 3、n-拷贝内存大小。
  11. 返回值:无。
  12. */
  13. void bcopy(const void *src, void *dest, size_t n);
  14. /* 功能:内存比较。
  15. 参数:1、s1-内存地址1。
  16. 2、s2-内存地址2。
  17. 3、n-比较的内存大小。
  18. 返回值:0-表示两块内存数据相同,非0-表示两块内存数据不同。
  19. */
  20. int bcmp(const void *s1, const void *s2, size_t n);

  第二组函数由mem开头,起源于ansi c,由任何支持ansi c标准的系统提供。

  1. #include <strings.h>
  2. /* 功能:内存初始化。
  3. 参数:1、str-内存地址。
  4. 2、c-初始化值。
  5. 3、n-内存大小。
  6. 返回值:0-表示两块内存数据相同,非0-表示两块内存数据不同。
  7. */
  8. void *memset(void *str, int c, size_t n)
  9. /* 功能:内存拷贝。
  10. 参数:1、src-源内存地址。
  11. 2、dest-目标内存地址。
  12. 3、n-拷贝内存大小。
  13. 返回值:无。
  14. */
  15. void *memcpy(void *dest, const void *src, size_t n);
  16. /* 功能:内存比较。
  17. 参数:1、s1-内存地址1。
  18. 2、s2-内存地址2。
  19. 3、n-比较的内存大小。
  20. 返回值:0-表示两块内存数据相同,非0-表示两块内存数据不同。
  21. */
  22. int memcmp(const void *s1, const void *s2, size_t n);

  除了上面的两组函数以为,string.h头文件定义了专门处理字符串的函数,这些函数以str开头比如strcpy/strcmp等,这些函数假设处理的字符串都以0结尾。

5.套接口ip地址到字符串转换函数

  1. #include <arpa/inet.h>
  2. /* 功能:将字符串IP地址转换成机器IP地址,。
  3. 参数:1、af-地址族:AF_INET, AF_INET6。
  4. 2、strptr-字符串ip。
  5. 3、addrptr-地址结构。
  6. 返回值:1-成功,0-针对af, strptr不是有效的ip地址格式,。
  7. */
  8. int inet_pton(int af, const char *strptr, void *addrptr);
  9. /* 功能:将机器IP地址转换成字符串IP地址,。
  10. 参数:1、af-地址族:AF_INET, AF_INET6。
  11. 2、addrptr-机器地址结构。
  12. 3、strptr-字符串ip。
  13. 4、size-strptr的长度,为了防止内核写溢出。
  14. 返回值:成功则返回指向strptr的指针,失败-返回null,并设置errno为相应值。。
  15. */
  16. const char *inet_ntop(int af, const void *addrptr, char *strptr, socklen_t size);

6. 套接口读写函数

  readn从一个socket中读取n个字节(引用unp示例代码):

  1. size_t Readn(int fd, void *vptr, size_t n)
  2. {
  3. size_t nleft;
  4. size_t nread;
  5. char *ptr;
  6.  
  7. ptr = vptr;
  8. nleft = n;
  9. while (nleft > ) {
  10. if ( (nread = read(fd, ptr, nleft)) < ) {
  11. if (errno == EINTR)
  12. nread = ; /* 系统调用被信号打断,重新调用 */
  13. else
  14. return(-); /* 发生异常 */
  15. } else if (nread == )
  16. break; /* 读到结束符,读到本地tcp收到的fin时 */
  17.  
  18. nleft -= nread;
  19. ptr += nread;
  20. }
  21. return(n - nleft); /* return >= 0 */
  22. }

  Writen向套接口描述符中写入n个字节(引用unp示例代码):

  1. /* 功能:从套接口中读取n个字节
  2. 返回值:n-成功;-1:失败*/
  3. size_t Writen(int fd, const void *vptr, size_t n)
  4. {
  5. size_t nleft;
  6. size_t nwritten;
  7. const char *ptr;
  8.  
  9. ptr = vptr;
  10. nleft = n;
  11. while (nleft > ) {
  12. if ( (nwritten = write(fd, ptr, nleft)) <= ) {
  13. if (nwritten < && errno == EINTR)
  14. nwritten = ; /* 被信号打断,再次调用 */
  15. else
  16. return(-); /* 写异常:write返回0-写一个tcp连接关闭的sock */
  17. }
  18.  
  19. nleft -= nwritten;
  20. ptr += nwritten;
  21. }
  22. return(n);
  23. }

  Readline从描述符fd中读取一行(引用unp示例代码):

  1. static int read_cnt;/* 缓冲区中可读的字节数 */
  2. static char *read_ptr;/* 当前缓冲区中可读字节的指针 */
  3. static char read_buf[MAXLINE];/* 读缓冲区 */
  4.  
  5. /* 功能:读去一个字节,
  6. 返回值:1-成功;0-读到文件结束符;-1-系统调用错误*/
  7. static ssize_t
  8. my_read(int fd, char *ptr)
  9. {
  10. if (read_cnt <= ) { /* 当缓冲区中可读字节数为0时,从文件中读数据到缓冲区 */
  11. again:
  12. if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < ) {
  13. if (errno == EINTR) /* 系统调用被信号打断 */
  14. goto again;
  15. return(-);
  16. } else if (read_cnt == ) /* 读到文件结束符,正常返回0 */
  17. return();
  18. read_ptr = read_buf;
  19. }
  20.  
  21. read_cnt--;
  22. *ptr = *read_ptr++; /* 从缓冲区中读一个字节给用户 */
  23. return();
  24. }
  25.  
  26. /* 功能:从fd中读取一行到vptr中,行的最大长度maxlen
  27. 返回值:n-成功,实际读取的字节数;-1:读失败*/
  28. ssize_t
  29. readline(int fd, void *vptr, size_t maxlen)
  30. {
  31. ssize_t n, rc;
  32. char c, *ptr;
  33.  
  34. ptr = vptr;
  35. for (n = ; n < maxlen; n++) {
  36. if ( (rc = my_read(fd, &c)) == ) {
  37. *ptr++ = c;
  38. if (c == '\n')
  39. break; /* newline is stored, like fgets() */
  40. } else if (rc == ) {
  41. *ptr = ;
  42. return(n - ); /* EOF, n - 1 bytes were read */
  43. } else
  44. return(-); /* error, errno set by read() */
  45. }
  46.  
  47. *ptr = ; /* null terminate like fgets() */
  48. return(n);
  49. }
  50.  
  51. /* 功能:从fd中读取一行到vptr中,行的最大长度maxlen
  52. 返回值:n-成功;-1-失败,并打印错误日志*/
  53. ssize_t
  54. Readline(int fd, void *ptr, size_t maxlen)
  55. {
  56. ssize_t n;
  57.  
  58. if ( (n = readline(fd, ptr, maxlen)) < )
  59. err_sys("readline error");
  60. return(n);
  61. }

  Readline存在的问题:因为间接使用了静态全局变量,因此Readline是不可重入的,不是线程安全的。

1.unix网络编程基础知识的更多相关文章

  1. C#网络编程基础知识

    C#网络编程基础知识一 1.IPAddress类 用于表示一个IP地址.IPAddress默认构造函数 public IPAddress(long address);一般不用 其中Parse()方法最 ...

  2. JAVA网络编程基础知识

    网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯.网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协 ...

  3. windows socket网络编程基础知识

    下面介绍网络7层协议在WINDOWS的实现: 7层协议 WIN系统 ________________________________________ 7 应用层 7 应用程序 ____________ ...

  4. 【Java基础】Java网络编程基础知识

    什么是网络编程 网络编程是通过使用套接字来达到进程间通信目的,那什么是套接字呢?其实套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的 ...

  5. JavaSE——网络编程基础知识

    计算机网络的分类: 局域网(LAN) 指在一个较小地理范围内的各种计算机网络设备互联在一起的通信网络,可以包括一个或多个子网,通常局限在几千米的范围之内. 城域网(MAN) 主要由城域范围内的各个局域 ...

  6. Windows网络编程基础知识

    1.WinSock的初始化 #include<iostream> #include<WinSock2.h> #include<MSWSock.h> #pragma ...

  7. python网络编程基础知识整理

  8. 大数据学习笔记——Java篇之网络编程基础

    Java网络编程学习笔记 1. 网络编程基础知识 1.1 网络分层图 网络分层分为两种模型:OSI模型以及TCP/IP网络模型,前者模型分为7层,是一个理论的,参考的模型:后者为实际应用的模型,具体对 ...

  9. Java网络编程基础(Netty预备知识)

    今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...

随机推荐

  1. 转: css3: display:box详解

    示例见:  css3: flexbox (BTW: blog不能包含iframe script真不方便啊~~) display:box;box-flex是css3新添加的盒子模型属性,它的出现可以解决 ...

  2. Qt容器类的对象模型及应用(线性结构篇)(好多图,比较清楚)

    用Qt做过项目开发的人,肯定使用过诸如QList.QVector.QLinkList这样的模板容器类,它们虽然名字长的不同,但使用方法都大致相同, 因为其使用方法都大体相同,很多人可能随便拿一个容器类 ...

  3. 一劳永逸让windows 64位操作系统 禁止强制驱动签名

    如何让WINDOWS7 64位直接加载“禁用强制驱动程序签名”方式启动  Windows Client 论坛 > Windows 7 问题 0 登录进行投票 因为开发需要,要装一台设备的驱动,但 ...

  4. 转:PAT练习题概览

    AT(pat.zju.edu.cn)是一个面向 C/C++程序的 Online Judge 系统.相比 ZOJ,HDOJ,POJ 等 ACM 题库,PAT 的题目非常基础,对于数据结构.算法的入门是比 ...

  5. 在Livemedia的基础上开发自己的流媒体客户端

    一.背景 二.Livemedia框架介绍 1.总体框架 2.客户端框架 2.1 客户端openRTSP流程 2.2增加一种新的媒体 2.2.1增加媒体的format 2.2.2 新媒体需要考虑的问题 ...

  6. C++_template_栈的链式存储及实现

    由于在C++数据结构中的代码不完整,特补全.等日后当工程库调用. 若有疑问,请留言. #include<iostream> using namespace std; template< ...

  7. eclipse主题插件

    打开eclipse ,选择 Help 选择Install New Software 点击 Add 输入http://eclipse-color-theme.github.com/update,选中Ec ...

  8. 两种MD5最后的值不一样,因为两种做法不一样

    //MD5加密 private static string Md5Hash(string input)         {             MD5CryptoServiceProvider m ...

  9. ansible模块

    ansible模块: 模块(Modules),类似于 "任务插件"("task plugins")或"库插件"("library ...

  10. 【Linux命令】Ubuntu14.04+QT5.2配置mysql

    安装qt: 官网下载qt5.2.1:qt-opensource-linux-x64-5.2.1.run 直接命令行运行:./qt-opensource-linux-x64-5.2.1.run 选择安装 ...