2023春季学期 网络编程技术 期末复习笔记

W3 3-1

UDP用途

  1. 只因你太美
  2. 简单的应答协议 DNS,DNS的16位ID用来请求应答匹配,太短了容易伪造
  3. 最新的QUIC协议

UDP socket套接字编程

五元组定义完整会话:2ports 2addr Protocol

字节序:尤其控制信息,统一使用网络字节序big endian

服务器常常需要bind地址,而客户端不必要。

tolen 对方地址长度

Bind和Socket首字母大写?表示对原有socket的封装,

MAXLINE长度后,recvline[n]=0来防止溢出

编程考虑问题:版本号、可靠、安全、性能、一致性…

UNP超时重传在编程实践中落后:1.粒度太粗,超时重传要几秒的检测,不可忍受 2.Risk Condition 依赖于事件发生次序?在不可靠网络中次序不定;andOS调度出去的时间超时等等

取而代之的是setsockopt建立读写超时即套接字选项

设计文件传输?可靠性 性能(并发性)

W5 3-1

代码分析角度

  • recv接入char数组,
    • 2-15 没有检查接受长度是否超过缓冲区(缓冲区应该是最大有效长度+1)
    • 2-15 没有用0手动截断导致转string或后续的溢出错误
    • 2-15 没有检查recv是否为有效长度(可能0)
    • 2-32 recv直接读入结构体,要检查格式化的正确与否(对齐等)

TCP SOCK_STREAM

Coding

Client

socket

connect 服务器地址

read or write

close

Server

socket

bind

listen(sock, backlog未决队列即未完成三次握手的SYN)

accept

read or write

close

Danger

  • 一个连接出错整个服务器退出syserr
  • 通过sigaction设置SIGIGN(SIGPIPE)来防止向已经关闭的socket写
  • 重启会导致address already in use,在关闭连接后还要等待2MSL 约 4min——设置SO_REUSEADDR允许2MSL内被绑定,可以多个服务器绑定一个端口但是本地IP地址不同
  • 设置SIGCHLD回收子僵尸进程 waitpid
  • 设置setsockopt的RCVTIMEOSNDTIMEO避免发带连接
  • KEEPALIVE保活报文测试
  • SOLINGER避免FIN阶段对方不回复的发呆连接,设置为1,0即可
  • SO_REUSEPORT 负载均衡每一个进程有一个独立的监听socket,并且bind相同的ip:port,独立的listen()和accept();提高接收连接的能力。
  • 一致性

安全性能

  • FTP慢:
    • 小数据包延迟大
      Nagle算法延迟发送直到ACK才发或者超时才发,避免一堆小包

延迟确认ack浪费,所以捎带确认,超时再ack

导致客户端先发一个包,然后下一个包太小不发送,服务器没收到请求不ack。直到服务器超时ack了,客户端才请求服务器才回应。

TCP_NODELAY来关闭NAGLE

TCP_CORK阻塞不足MSS的小包,手动开关来控制

SO_RCVBUF SNDBUF手动修改缓冲大小,在连接前设置,至少4个MSS

进程并发服务器

主进程loop accept+fork新进程处理连接,主进程要关闭acceptedsocket而子进程关闭监听socket。

prefork先fork一堆来监听,一个accept处理。。。其他接着听

主进程accept通过socket传输

子进程各个accept通过进程锁保证互斥(文件锁或者进程mutex),给fd上锁

避免accept的重复堵塞 BSD acceptfilter系统SO_ACCEPTFILTER SOL_SOCKET

Linux:TCP_DEFER_ACCEPT

accf_data: listen socket在收到client的数据后才可读

accf_dns: listen socket在收到一个完整的DNS请求后才

可读

accf_http: listen socket在收到一个完整的HTTP请求后才可读

UDP SOCK_DGRAM

Coding

地址中含地址+端口

int sock = socket(AF_INET,SOCK_DGRAM, 0)

申请套接字描述符

bind(mysock, (sockaddr *) myaddr,sizeof(myaddr));

绑定套接字到端口,服务端

int sendto(int sd, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

发送消息到指定地址

int recv(int sd, void *buf, size_t len, int flags);

int recvfrom(int sd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

Client

socket

sendto 自动选择己方端口,需指明对方地址

recvfrom ??从某个特定地址接受,如果不在乎对方地址设置地址和addrlen为null。也可能是放来源地址的

Server

1
2
3
4
5
6
7
8
int mysock;
struct sockaddr_in myaddr;
mysock = socket(AF_INET,SOCK_DGRAM,0);
memset(&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons( 53 );
myaddr.sin_addr = htonl( INADDR_ANY );
bind(mysock, (sockaddr*) myaddr,sizeof(myaddr));

INADDR_ANY 即0.0.0.0 监听任何来的地址
socket

bind

recvfrom

sendto

Danger

问题

可靠性

数据丢失,recv阻塞等待——设置超时重传时间

不要用alrm设置超时,被调度出运行且超时

使用选项setsockopt

int setsockopt( int sockfd, int level, int optname,constvoid *opval, socklen_t optlen);

SO_RCVTIMEO, SO_SNDTIMEO

指定时间没有可读就结束返回错误

指定时间数据没法出去结束返回错误

int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *);限时读,直到有fd可读

FD_ISSET fd在fdset中的状态是否变化为可读

数据对应(可靠)

标识号

Final

lect1

互联网的经验:

问题驱动 成本低 核弹和分布式系统

遵循规律 18 吉尔德定律主干网带宽6 n^2

演化成长 接入终端丰富 协议栈演化 绝非偶然

OSI:权威机构ISO 严谨设计 区分服务协议接口

互联网侧重异构网络的互联

lect2 协议设计

角色:客户端、xx类服务器

体系结构:颗粒化:控制、数据分离) 传输统一(相同传输层) 功能选项化(考虑到但不实现)

状态:一问一答,有依赖的一问一答

PDU协议数据单元一次会话传递:边界!TCP数据流切割为各个协议原语、UDP报文和原语的对应——固定格式与分隔符、指定可变长度、传输层指定一次TCP连接和报文

编码、传输

协议设计准则

  • 原语、消息类型(状态)编号:区分原语,指示错误
  • 一定要有标准的协议规范否则任何都是合法
  • 并发性能:区分多个用户的多次服务UDP,区分一个用户两次服务
    • 会话ID
    • 指定会话开始的位置
  • 性能
    • 分块ID
  • 认证安全
    • UDP定义协议规范,能够判定是否合规,丢弃不合规
    • 认证信息
    • 压缩加密
  • 可扩展性
    • 协议版本号
    • 报文类型
    • 扩展项
  • 编码
    • 区分控制信息和内容信息——规定文本格式
    • 序列化(字节序!!)
  • 可靠传输
    • 校验和
    • hash值
  • 请求应答匹配
    • ID

Protobuf

默认小端字节序

[rule] [type] [name] = [tag]

message rsp{

required bytes fuck = 1;

}

7bit varint 首bit为0表示下一个是新信息,否则是1表示下一个也用来表示此数字

udp性能好并发好,复杂

tcp简单,可靠但固化

http最简单 性能差

QUIC优化TCP+TLS??更快但等价

简易

可扩展和演化

互操作性

lect3 UDP编程

8B 包头 无连接 多路复用 可选差错控制

没有拥塞、流量控制

支持组播广播 DNS

不可靠:丢包、乱序、抖动、内容改变

信号系统:异步事件

INT 用户前台

KILL杀死

HUP 重启

QUIT建立core的退出

WINCH   忽略信号     窗口大小发生变化

SIGALARM   终止进程     计时器到时

hup disconnet时

lect4 UDP编程2

安全性

假冒服务器回复

拒绝服务攻击

一致性

切割协议的字符

长度限制:MTU MAXLINE UDP

长请求所有数据返回还是部分返回

recv中的MSG_TRUNC flag,返回截断前的长度

服务器尽早识别不合法用户,区分业务类型和时间

客户端分担服务器功能,尽量过滤异常请求

224.0.0.1子网所有系统 .2为所有路由器

IGMP协议报告组播组的加入与退出,路由器定期询问主机响应,不主动退出而是询问发现

lect5 TCP网络编程

不支持组播广播

lect7 多线程编程

int pthread_create( pthread_t tid, pthread_attr attr, void ( start_routine )(void), void* arg )

pthread_detach 从主控线程分离,自己回收资源,防止僵尸。否则要等待主控线程join

pthread_attr 线程属性

如PTHREAD_SCOPE_PROCESS进程级或系统级争cpu

gethostbyname线程不安全,使用全局变量指针,后面把前面覆盖,不要用!用两次或者多线程用都不安全——gethostbyname_r

共享数据的data race多个写,非原子的单个写(经常一条编译成多条),指令重排(线程内不冲突就重排但是线程间冲突)——使用锁和条件变量

使用mutex时的死循环甚至死锁 ??

图片

pre threaded

  • 主线程accept 通过工作队列(命名队列)传递
  • 各个子进程accept通过互斥锁保证互斥

lect8 Event多路复用

select

select检测事件:可读、FIN(read返回0,endoffile)、RST的read-1、accept

可写或者写端关闭

用于TCP Server的select,一开始只有listen fd用于监听accept可接受新connfd。

  • 套接字数目有限制 FDSETSIZE 1024无法更改——poll没有限制size,
    int poll(struct pollfd *fdarray, unsigned long nfds, int timeout); /return : count of ready descriptors, 0 on timeout, -1 on error/

pollfd包含fd,events和recents,指定验证的条件

  • 阻塞非阻塞读写 阻塞IO
    非阻塞io file control

val = fcntl(sockfd, F_GETFL, 0);

fcntl(sockfd, F_SETFL, val | O_NONBLOCK);

用这玩意取出老状态bit,与上新状态位再放回去。图片

  • 进一步提高效率epoll不用来回copy fdset
    • ET EPOLLET 只有监视句柄发生事件才报告,必须非阻塞读写防止一个阻塞饿死所有其他。纯正的事件触发
    • 当read(2)或者write(2)返回EAGAIN时才需要挂起?
    • LT 默认,阻不阻塞都支持,传统的select。检测缓冲区有没有没读完的东西
      epoll create()创建空监控fd table

ctl(ADD)

epoll wait

工作方式:边缘触发ET或者等级触发LT

lect9 IPV6

任播取代广播

服务器纯v6 通过把v4地址映射到v6来服务v4

0:0:0:0:0:FFFF:210.25.132.100

全0作为srcaddr可以,不能目的,不能分配

IN6ADDR_ANY_INIT

loop地址:::1 给自己发,只能节点内部,类似loopback

inet_pton和aton

协议无关服务器编程

同时监听v4和v6

基于域名的切换 resolver查A和AAAA

getaddrinfo替代gethostbyname,域名或ip地址,返回一个链表多个地址

sockaddr_storage足够大的通用地址存储结构

16B的sockaddr

28B的sockaddr in6

128B的sockaddr storage

getnameinfo 解析sockaddr给出的域名或者服务名,返回host和service

n是规范化格式 aton ntop等等

IPV6_V6ONLY

lect10 安全编程1

协议缺陷

内存操作

缓冲区溢出

动态内存操作

读越界

缓冲区溢出,不要相信不可靠信息

整数安全

溢出、截断、符号

lect11 安全编程2

格式化字符串

格式化的字符串 不能交给用户,因为参数可变长可以偷栈信息

代码注入

command命令中含有用户的string

race condition

mysql_real_escape_string不能解决所有问题

使用prepare类型的调用接口,可以将数据和控制分开

使用’?’来作为占位符号,实际用时填入相应的数据

使用ORM 对象关系映射(Object Relational Mapping),将数据库中的数据映射为对象

ValgrindDRD 用于检测多线程编程中的常见错误

lect12

lect13

代码挑错

图片

图片

图片
图片

可能可以read但是无法readline