IP和端口
- 端口:相当于消息接收者对应进程的名字。找到端口就能找到对应的应用程序(短整型 16 为 unsigned short 能够使用的范围 0~ 65535)
IP地址能够找到一台电脑
例子:一个快递员送快递,IP地址相当于快递的地址,快递员到达地址对应的屋子后,发现屋子有四个人,他怎么知道快递给谁,就是通过端口号,来确定快递该给谁。
网络分层模型(OSI或ISO)
字节序
- 大端:高-》低 ,低-》高;
- 小端:低-》低,高-》高;
- 例子:0x12 34 56 78
- 大端:12 34 56 78
- 小端:78 56 34 12
字节序转换
- uint16_t htons(uint16_t hostshort) 将一个短整型从主机字节序-》网络字节序
- uint16_t ntohs(unit16_t netshort) 将一个短整型从网络字节序-》主机字节序
IP地址转换
- 主机字节序转换为网络字节序
- 主机IP地址是字符串,网络IP地址是整形
#include<arpa/inet.h>
int inet_pton(int af,const char *src,void *dst);
- af 地址族(IP地址家族包括ipv4,ipv6)协议
- AF_INET:ipv4格式
- AF_INET6 :ipv6格式ip地址
- src:传入参数,对应要转换的点分十进制的IP地址:192.168.1.100
- dst:传出参数,函数调用完成,转换得到的大端整型IP被写入这块内存中
- 返回值:成功返回1,失败返回0或-1;
网络字节序转换为主机字节序
const char *inet_ntop(int af,const void *src,char *dst,socklen_t size)
- src :存储传入的大端IP地址
- dst:传出参数,存储转换得到的小端点分十进制的IP地址
- size :修饰dst参数的,标记dst指向的内存中最多可以存储多少个字节。
- 返回值
- 成功返回不为空的dst的内存地址,通过返回值也可以直接取出转换得到的字符串
- 失败:NULL
- 点分十进制IP->大端整形
in_addr_t inet_addr(const char *cp);
- 大端整形-》点分十进制
char* inet_ntoa(struct in_addr_in);
服务端:
socket()
服务端调用socket()函数,创建ipv4网络协议,传输协议为TCP。
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
- domain: ip地址格式:AF_INET;
- type:指定传输层协议 SOCK_STREAM(流式协议) 与TCP连用 SOCK_DGREAM(报式协议)UDP连用
- proticol:与type中指定的传输协议配套使用;为流式0,TCP 报式 0 UDP
- 返回值:
- 成功:返回文件描述符
- 失败:-1;
bind()
调用bind()函数,给socket绑定ip地址和端口号
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
accept()
进入监听状态后调用accept()函数,获取客户端的连接,没有连接会阻塞等待客户端连接的到来(默认阻塞);
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)
- addr 传出参数,
- addrlen 传入传出参数,整形:指向的内存大小
- 返回值:
- 成功:文件描述符 用于通信的;
- 失败 -1;
客户端:
socket()
- 客户端创建号socekt后,bind()函数绑定后,
-
connect·()
调用connect函数发起连接,该函数指明服务端的IP地址和端口号,TCP三次握手开始
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
- 都是传入参数,通信套接字,
- 返回值:绑定成功,需要绑定,随机分配一个空闲的端口。0;失败-1;
- 连接建立好后,客户端和服务端开始相互传输数据,
- read()和recv();
ssize_t read(int sockfd,void *buf,size_t size);
ssize_t recv(int sockfd,void *buf,size_t size,int flags);
- sockfd: 用于通信的文件描述符,accept的返回值
- buf:指向一块有效内存,用于存储接收数据
- size:参数buf指向的内存的容量
- falgs:特殊的属性,一般不用,指定为0;
- 返回值:
- 大于0;实际接收的字节数;
- 等于0;对方断开了连接;
- 如果连接没有断开,接收端接收不到数据,接收端的函数就会阻塞等待数据到达,数据到达后函数接触阻塞,开始接收数据。当发送端断开连接,接收端无法接收到任何数据,但是这时候就不会阻塞了,函数直接返回0;
write 和 send();
ssize_t wirte(int fd,const *buf,size_t len)
ssize_t send(int fd,const void *buf,size_t len,int flags)
- fd :通信文件描述符,accept()函数的返回值
- buf:传入参数,要发送的字符串
- len:要发送字符串的额长度
- flags:不用,指定为0;
- 返回值:
- 大于0:实际发送的字节数,和参数len是相等的;
- -1:发送数据失败了
- 关闭套接字close();
#include<unistd.h>
int close(int fd);
socket维护了两个队列
- 一个是还没完全建立连接的队列,叫TCP半连接队列,这个对列还没有完成三次握手服务端处于syn_rcvd状态
- 一个是已经建立连接的对列,称为TCP全连接对列,这个对列完成了三次握手的连接,此时服务端处于established状态。
- 当TCP全连接对列不为空后,服务端的accept()函数,就会从队列中拿出一个已经为你撑连接的socket返回应用程序,后续的数据传输都用这个socket。