alin的学习之路(Linux网络编程:一)(网络模型、帧格式、socket套接字、服务器端实现)

1. 协议

协议是一组规则,规定了如何发送数据。通信的双发都需要遵守该规则

2. 网络分层结构模型

1. OSI七层模型

物:物理层

数:数据链路层

网:网络层

传:传输层

会:会话层

表:表示曾

应:应用层

2. TCP/IP模型

链:数据链路层(网络接口层):以太网帧协议,ARP协议

网:网络层:IP协议,ICMP协议,IGMP协议

传:传输层:TCP协议,UDP协议

应:应用层:HTTP,ftp,nfs,telnet,ssh

3. 网络通信过程

数据在没有被封装之前,是不能传输到网络上去进行通信的。

数据产生后,经由数据链路层、网络层、传输层、应用层,四层每一层都会对数据进行不同的封装,封装后才能传送到网络中进行通信。

到达目的端时,经由应用层、传输层、网络层、数据链路层,四层每一层都会解封装,从而最终得到通信数据。

4. 网络协议格式

1. 以太网帧格式

  • 以太网规定,数据包必须从一块网卡,传送到另一块网卡。

  • 目的地址、源地址 —— mac地址(ifconfig命令可查看)。网卡编号。全球唯一。

  • ARP协议:
    根据 IP地址, 获取 mac 地址。

    ARP 包括请求和应答,请求携带发送端的 mac 地址,通过应答的信息来获取目的端的 mac 地址

2. IP段格式

  • 版本:IPv4、IPv6
  • TTL: time to live:防止路由包拥塞,表示最大路由跳数
    • 用来设置数据包,在路由节点中的跳转上限。每经过一个路由节点,该值 -1。
    • 减为0的路由节点, 有义务将该数据包丢弃。 防止拥塞网络。
  • 源IP地址:32位 —— 4字节。 192.168.1.4 —— 点分十进制 IP地址。本质 是一个 string。 为人类看。
  • 目的IP地址:32位 —— 4字节

3. UDP协议格式

  • 源端口号:16位。2^16 — 取值范围:0 ~ 65535
  • 目的端口号:16位。2^16 — 取值范围:0 ~ 65535
  • IP地址:在 网络环境中, 唯一标识一台主机。
  • port:在 网络主机上,唯一标识一个进程。
  • IP+port :在 网络环境中, 唯一标识一个 进程。 —— socket 套接字。

4. TCP协议格式

  • 源端口号:16位。2^16 — 取值范围:0 ~ 65535
  • 目的端口号:16位。2^16 — 取值范围:0 ~ 65535
  • 32位序号。
  • 32位确认序号。
  • 6个标志位。
  • 16位窗口大小。---- 取值范围:0 ~ 65535

TCP 优先建立好连接,后续消息全部在该路径上发送,UDP则是每次传输的路由路径不同

5. 网络应用程序设计模式

C/S 模式(client/server):

  • 优点:提前缓存数据,提高通信效率。协议选择灵活。开发调试较方便。
  • 缺点:对用户安全性构成威胁。开发工作量大。不能跨平台。
  • 应用场景:对数据安全、稳定要求较高场合。需要提前缓冲数据。

B/S 模式(browser/server):

  • 优点:不需要额外向用户主机安装应用。开发工作量相对小。跨平台使用。
  • 缺点:不能缓存大量数据。协议选择不灵活。调试不方便。
  • 应用场景:需要跨平台。

6. 网络套接字

在一次通信中,套接字socket必须成对出现

一个文件描述符指向一个套接字 ( 该套接字内部由内核借助两个缓冲区实现。)

1. 网络字节序

小端法:(pc本地存储,IA架构)高位存高地址,低位存低地址。
大端法:(网络存储)高位存低地址,低位存高地址。

需要在做网络传输时,将 “网络字节序” —— “本机字节序” 相互转换。

#include <arpa/inet.h>h表示host,n表示network,l表示32位长整数,s表示16位短整数。// 本地字节序(IP) ---> 网络字节序(IP)
uint32_t htonl(uint32_t hostlong);
参数:要求是一个 int, 但 “192.168.6.108” 是 string。——> atoi() --> int --> htonl() --> 网络字节序。// 本地字节序(port) ---> 网络字节序(port)
uint16_t htons(uint16_t hostshort);// 网络字节序(IP) --->本地字节序(IP)
uint32_t ntohl(uint32_t netlong);// 网络字节序(port) --->本地字节序(port)
uint16_t ntohs(uint16_t netshort);

2. IP地址转换函数

#include <arpa/inet.h>
// 将 本机字节序 string 类型的 IP地址, 转换为 网络字节序 数值类型
int inet_pton(int af, const char *src, void *dst);af:AF_INET(IPv4)、AF_INET6(IPv6)src:传入。IP地址(string类型,点分十进制)dst:传出。转换后的数值类型的 IP地址。返回值:1:成功。0:语法正确, IP无效。-1:失败。
// 将 网络字节序 数值类型, 转换回程 本机字节序 string。
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);af:AF_INET(IPv4)、AF_INET6(IPv6)src:传入。网络字节序 IP地址。dst:传出。string 类型 IPsize:dst 的大小。返回值:成功:dst 传出值。string 类型的 IP地址。失败:NULL

3. sockaddr 数据结构

使用 man 7 ip 查看 struct sockaddr_in 结构体类型

struct sockaddr_in {sa_family_t   sin_family; /* address family: AF_INET */in_port_t    sin_port;  /* port in network byte order */struct in_addr sin_addr;  /* internet address */
};/* Internet address. */
struct in_addr {uint32_t    s_addr;   /* address in network byte order */
};// 举例:
struct sockaddr_in addr;  // 定义地址结构(IP+port)变量
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
// #define INADDR_ANY 0 --- 取系统中任意一个 有效的 IP地址。
addr.sin_addr.s_addr = htonl(INADDR_ANY);// 模拟,使用 bind 绑定地址结构。
bind(lfd, (struct sockaddr *)&addr, sizeof(addr));

7. socket 通信流程图

8. socket 通信函数

1. socket 函数

#include <sys/types.h>  该头文件包含在 <unistd.h>
#include <sys/socket.h>// 创建一个新的套接字。
int socket(int domain, int type, int protocol);domain: AF_INET、AF_INET6、AF_UNIXtype:SOCK_STREAM、SOCK_DGRAMprotocol:通常传 0返回值:成功:返回新套接字对应的 fd失败:-1,errno

2. bind 函数

#include <sys/types.h>  该头文件包含在 <unistd.h>
#include <sys/socket.h>// 给 socket 绑定IP+port
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockfd:socket函数的返回值。———— 监听套接字。addr: 地址结构体。 ———— 类型应该是:  struct sockaddr_in ,函数调用时需要强制类型转换addrlen: sizeof(addr) 地址结构大小返回值:成功:0失败:-1, errno

3. listen 函数

#include <sys/types.h>
#include <sys/socket.h>// 设置同时与服务器建立连接的 上限数。———— 该函数不会阻塞程序。
int listen(int sockfd, int backlog);sockfd: socket 函数的返回值backlog: 设置的监听最大数。 128返回值:成功:0失败:-1, errno

4. accpet 函数

// 阻塞等待客户端连接请求。 成功的话,返回一个与客户端成功建立连接的,用于通信的 socket文件描述符
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);sockfd: socket 函数的返回值addr: 传出参数。 成功与服务器建立连接的那个客户端地址结构。addrlen:传入传出参数。入:addr的大小。出:客户端 addr 的实际大小。举例:socklen_t clt_addr_len = sizeof(addr);  &clt_addr_len返回值:成功:能与客户端进行数据通信的 socket 对应的 文件描述符。失败:-1, errno

5. connect 函数

// 使用现有的 socket 与服务器建立连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockfd: socket 函数的返回值addr:传入参数。 服务器的地址结构。addrlen: 服务器地址结构的大小。返回值:成功:0失败:-1, errno
———— 客户端自己的地址结构,没有指定,由系统自动分配 --- "隐式绑定"

9. TCP-CS 模型通信

1. server端

通信流程:

  1. socket函数创建监听套接字
  2. bind函数绑定IP地址和端口号
  3. listen函数设置最大监听上限
  4. accept函数阻塞等待客户端接入服务器,返回值传出客户端的IP地址和端口号
  5. read函数从accept的返回值文件描述符中读数据
  6. 数据处理:小写转大写,使用toupper函数
  7. write函数写回到accept的返回值,即写回到客户端
  8. close函数关闭文件描述符

编码实现:

#include <stdio.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ctype.h>#define SRV_PORT 9527
#define SIZE 4096int main()
{int lfd = 0;struct sockaddr_in srv_addr,clt_addr;srv_addr.sin_family = AF_INET;srv_addr.sin_port = htons(SRV_PORT);srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//1.创建套接字lfd = socket(AF_INET,SOCK_STREAM,0);if(-1 == lfd){perror("socket");return 1;}//绑定IP和portint ret = bind(lfd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));if(-1 == ret){perror("bind");return 1;}//listen 设置最大监听数ret = listen(lfd,128);if(-1 == ret){perror("listen");return 1;}printf("服务器阻塞等待客户端接入\n");//accept 阻塞等待客户端接入socklen_t clt_addr_len = sizeof(clt_addr_len);int cfd = accept(lfd,(struct sockaddr*)&clt_addr,&clt_addr_len);if(-1 == cfd){perror("accept");return 1;}char ip[SIZE] = {0};printf("客户端接入:ip:%s,port:%d\n",inet_ntop(AF_INET,&clt_addr.sin_addr.s_addr,ip,SIZE),ntohs(clt_addr.sin_port));char buf[SIZE] = {0};  while((ret = read(cfd,buf,SIZE)) != 0){if('\0' != buf[strlen(buf)-1])buf[strlen(buf)-1] = '\0';for(int i=0 ;i<ret ;++i){buf[i] = toupper(buf[i]);}write(cfd,buf,ret);printf("client:%s\n",buf);memset(buf,0,SIZE);}close(cfd);close(lfd);return 0;
}

2. client端

通信流程:

  1. socket函数创建通信套接字文件描述符
  2. connect函数与服务器建立连接,传入的参数中addr表示服务器的IP地址和端口号
  3. 从标准输入中读字符串,将该字符串通过write写到通信套接字中
  4. read函数从服务器读处理后的数据
  5. 结束后关闭文件描述符

编码实现:

#include <stdio.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ctype.h>#define SRV_PORT 9527
#define SIZE 128int main()
{int cfd;cfd = socket(AF_INET,SOCK_STREAM,0);if(-1 == cfd){perror("socket");return 1;}struct sockaddr_in srv_addr;srv_addr.sin_family = AF_INET;srv_addr.sin_port = htons(SRV_PORT);inet_pton(AF_INET,"127.0.0.1",&srv_addr.sin_addr.s_addr);int ret = connect(cfd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));if(-1 == ret){perror("connect");return 1;}printf("客户端连接成功\n");char buf[SIZE];while(1){fgets(buf,SIZE,stdin);ret = write(cfd,buf,SIZE);ret = read(cfd,buf,SIZE);if(0 == ret){printf("客户端退出\n");break;}}close(cfd);return 0;
}

alin的学习之路(Linux网络编程:一)(网络模型、帧格式、socket套接字、服务器端实现)相关推荐

  1. java实现套接字网络编程_Java网络编程(一)Socket套接字

    一.基础知识 1.TCP:传输控制协议. 2.UDP:用户数据报协议. 二.IP地址封装 1.InetAddress类的常用方法 getLocalHost() 返回本地主机的InetAddress对象 ...

  2. Java网络编程(一)Socket套接字

    一.基础知识 1.TCP:传输控制协议. 2.UDP:用户数据报协议. 二.IP地址封装 1.InetAddress类的常用方法 getLocalHost() 返回本地主机的InetAddress对象 ...

  3. linux网络编程二:基础socket, bind, listen, accept, connect

    linux网络编程二:基础socket, bind, listen, accept, connect 1. 创建socket #include <sys/types.h>     #inc ...

  4. tcp/ip网络编程--accept()函数返回的套接字

    tcp/ip网络编程–accept()函数返回的套接字 套接字:1)套接字是对网络中不同主机的应用进程之间进行双向通信的端点的抽象:一个套接字就是网络进程通信的一端.[1] 2)套接字是用来与另一个进 ...

  5. week6 day1 网络编程之扫盲知识与套接字介绍

    week6 day1 网络编程之套接字介绍 一. 客户端/服务器架构 二. OSI七层 2.0 引子 2.1 OSI七层协议 三. tcp/ip五层模型讲解 3.1 物理层(高低电位传输) 3.2 数 ...

  6. 【网络编程】---C++实现原始套接字捕获数据包

    C++实现原始套接字捕获数据包 引言 原始套接字与TCP套接字和UDP套接字的区别 原始套接字编程使用的场合 原始套接字的通信过程 (1)基于原始套接字的数据发送过程 (2)基于原始套接字的数据接收过 ...

  7. 小白学习之路,网络编程(上)

    一,计算机网络基础 在讲网络编程之前,先跟大家简单的介绍一下一些网络相关的知识. 在最早之前,两台电脑之间通信是通过电脑的mac地址找到对方,并实现相互通信.当然每台电脑都只存在唯一的mac地址,在生 ...

  8. Python之路(第三十一篇) 网络编程:简单的tcp套接字通信、粘包现象

    一.简单的tcp套接字通信 套接字通信的一般流程 服务端 server = socket() #创建服务器套接字server.bind() #把地址绑定到套接字,网络地址加端口server.liste ...

  9. linux网络编程:使用多进程实现socket同时收发数据

    转载:http://blog.csdn.net/li_wen01/article/details/52685844 前面已讲过使用一个进程实现服务端和客户端P2P通信的实例,但是它只能同时处理一个客户 ...

  10. python网络编程--创建简单的UPD套接字实现两个进程间互相通信

    Socket 什么是socket Socket通常又称"套接字",应用程序通常会通过"套接字"实现向网络发出请求或者相应网络请求,从而实现不同计算机之间或同一计 ...

最新文章

  1. Chromium之各国语言切换
  2. 长江存储推消费级固态硬盘,Xtacking技术加持
  3. 浪潮存储linux登录密码,登录存储系统CLI管理界面(用户名+密码)
  4. 零基础可以学python吗-没编程基础可以学python吗
  5. 把一个人的特点写具体作文_把一个人的特点写具体500字作文
  6. 阿里2019实习内推,五轮技术面+一轮HR面,Java岗面经
  7. 从壹开始学习 NetCore 新篇章 ║ Blog.Core 开发社之招募计划书
  8. [复变函数]第17堂课 5 解析函数的 Laurent 展式与孤立奇点 5. 1 解析函数的 Laurent 展式...
  9. Hbase导入、导出数据到本地文件
  10. 如何在HTML中加载一个CSS文件?
  11. 计算机领域国际会议分类及排名
  12. 进阶 | 产品失效模式与效益分析(DFMEA)的实际应用
  13. sql中多表连接查询——自连接
  14. MindManager下载和使用
  15. Elastic-Job使用
  16. 1113. Integer Set Partition (25)
  17. [PTA]习题3-5 三角形判断
  18. Unity 3D:接入原生广告(UnityAds)和 GoogleAdmob 和 Vungle
  19. 手把手教你用Python网络爬虫实现上海证券交易所定期报告pdf文件下载(附代码)...
  20. 工作室课题学习情况总结(第一周)

热门文章

  1. 小学信息用计算机绘画教案,小学信息技术《初识画图》教学设计
  2. 利用 队列 来实现医院挂号模拟看病系统(c++,顺序及链式)
  3. PAT、PMT、SDT详解 MPEG2-TS流的分析
  4. 狼人杀服务器紧急维护中,狼人杀被炸身份怎么办?不要慌,催眠自己,你是一个大水民!...
  5. 强力推荐的18种CSS命名和书写规范
  6. 外盘期货正大国际:如何控制期货交易的风险
  7. 利用互相关求时差---xcorr
  8. python ubuntu word txt 转pdf,python实现pdf转换成word/txt纯文本文件
  9. Cocos 3D 出席云展会,简直帅呆了!
  10. 波动溢出模型|GARCH、DCC、BEKK