1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > alin的学习之路(Linux网络编程:一)(网络模型 帧格式 socket套接字 服务器端实现)

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

时间:2023-01-25 07:34:42

相关推荐

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

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、IPv6TTL: 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 ~ 65535IP地址:在 网络环境中, 唯一标识一台主机。port:在 网络主机上,唯一标识一个进程。IP+port :在 网络环境中, 唯一标识一个 进程。 —— socket 套接字。

4. TCP协议格式

源端口号:16位。2^16 — 取值范围:0 ~ 65535目的端口号:16位。2^16 — 取值范围:0 ~ 6553532位序号。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+portint 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端

通信流程:

socket函数创建监听套接字bind函数绑定IP地址和端口号listen函数设置最大监听上限accept函数阻塞等待客户端接入服务器,返回值传出客户端的IP地址和端口号read函数从accept的返回值文件描述符中读数据数据处理:小写转大写,使用toupper函数write函数写回到accept的返回值,即写回到客户端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端

通信流程:

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

编码实现:

#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;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。