网络编程
2016-1-27:
网络协议: 用结构体(报头)来描述数据包的属性与内容来处理
- 有源IP和目标IP
- 网络协议层层封装 HTTP <- TCP <- IP <- 机器属性
- IP 同时被 TCP 和 UDP 封装 , IP本身是不可靠的
- TCP以应用层的技术,保障了协议的可靠程度
B/S方式:
HTTP: 通用的客户端协议 让任何人可以轻易的使用浏览器来访问服务端
C/S方式:
TCP:
1. TCP在拥堵时会自动收缩自己的传输范围(用数学公式推算的)
2.【滑动窗口】技术:不断清除返回的队列内容来缩减队列
3. 基于流的连续传输:保障所有的包按顺序到达
UDP: 不保障可靠和顺序,但是效率高
短链接:瞬间断开
长链接:一直建立等待超时再断开的链接
socket:
一个描述符 代表的是一片内存区域
含有接受缓存和发送缓存 通过内核将信息发到网卡上发送
— domian 使用AF_UNIX 可以使用本地模式 使用本地套结字通信
— 多进程的socket之间没有意义,但是fork()是可以放在accept之前的,但是要用AF_UNIX来渡过去
单个主机内,每一个进程需要采用不同的端口号来使用与标识
客户端的端口号可以人为指定也可以系统分配
上下限:(0-65535)short类型
netstat -antplue #shell查看各种有关端口的信息
tcpdump port <端口号> -i <网卡> -nn #查看端口的信息
DDOS攻击: 不断地连环ping目标网卡 然后使之缓存区立即溢出 无法接受数据
— 低级 有效 不可防范
TCP编程:
eg:
https://github.com/KhalilWang/IPC_samples/tree/master/Socket
客户端:
1.socket(AF_INET, SOCK_STREAM, 0); //创建套结字
2.connect(sockfd, *sa_addr, addrlen); //连接端口
3.send() / recv(); //发送/接受数据
4.close(fd); //退出
服务端:
1.socket(AF_INET, SOCK_STREAM, 0); //创建套结字
2.bind(sockfd, *sa_addr, addrlen); //绑定端口
3.listen(sockfd, backlog); //监听端口
循 4.accept(sockfd, *addr, *addrlen); //等待接受请求
环 5.fork() / pthread_create(); //创建进线程处理
6.close(fd); //退出
listen中的backlog是可以允许的排队人数
— 真正的人数是 backlog * 3 / 2 + 1
accept中的addr接收的是连接人的addr信息
accept返回的是一个sockfd!CS之间的通信由此开始!
通常在accept之后创建进线程来处理新的fd,
然后主进程继续accept来接受请求
进程立即fork()(线程没有独立空间,不太适合存储accept的返回值)
线程则需要将fd存储于栈上,使用同步技术防止fd被覆盖
对于描述符的使用:(适合tcp, 但是这两个函数和协议无关)
send(sockfd, *buf, len, flags);
recv(sockfd, *buf, len, flags);
其实就是对于read() / write()的封装
struct sockaddr{
sa_family_t sa_family ;
char sa_data[14];
} //原版的sockaddr地址信息
新版: sockaddr_in 将sa_family 转换为连续的几个变量
sa_family_t -> sin_family + sin_port(short) + sin_addr(32位整形)
宏定义的##意思是将后面的的变量转为字符补在后面
*一般采用sockaddr_in 强转为 sockaddr 使用
端口信息采用了网络字节序:
htonl()/htons() host to network long/short 将端口号进行网络字节序转化
sin_addr -> 这个结构体变量是宏定义: 接受本网段还是全网的连接
IP地址是字符串的地址 是一个16字节的char数组 存储的IPV4的字符串
使用inet_aton()来将字符串进行网络字节序转换 inet_ntoa()反之
*tip: 网络传输时一定要注意内存对齐!
粘包问题:收包的时候没有区分好数据边界
解决方法:发每一个包之前自己做一个报头,分两次收报头,再收数据:
struct head{
int ctl;
int size;
}
根据size 的值收第二个包 防止粘包现象