服务端/客户端模型
c/s:c专用 标准协议/自定义协议 资源大部分在客户端,服务器只进行关键交互数据的收发,功能比b/s复杂
b(browser)/s:c通用 使用http协议 资源由服务器发给客户端 受http限制,功能不复杂
p2p:下载类软件和直播类软件 客户端从服务器下载数据的同时也会给后面的客户端上传数据
TCP(传输控制协议)
特点
1.有链接:在发送消息前,会先打通一条链路,在一次会话中,链路不会改变,
客户端和服务端可以检测对方是否关闭,当一端关闭时,另一端也会关闭
2.可靠传输:应答机制,自动重传
3.流式套接字,数据连续且有顺序,数据间无边界,会有粘包问题
解决:1.设置边界,2.固定大小,3.自定义协议
4.全双工通信:有两个缓冲区,每个大小64K,缓冲区满会阻塞,收发互不影响
三次握手,四次挥手
TCP 是有连接的通信过程,需要三次握手建立链接。
两台主机之间的通信链路建立需要如下过程:
主机1 -----syn-----》主机2
主机1 《---ack syn--- 主机2
主机1 ----ack -----》主机2
通过抓包来验证三次握手:
1、 tcpdump -n -i lo tcp port 9999 ===>S S. .
2、 wireshark 规则: tcp.port == 9999 ===>syn syn ack ack
问题:
三次握手分别是在服务器和客户端的那个函数上完成。
结论:
客户端函数:connect()
服务器函数:listen()
四次挥手
主机1 --- F A ---》主机2
主机1 《---A ----- 主机2 主机1 不在发送消息,但是有可能接受消息
主机1 《---F A --- 主机2
主机1 ----A ----》主机2 主机1 2 全部完毕
TCP编程步骤
int listen(int sockfd, int backlog);
功能:在参数1所在的套接字id上监听等待链接。
参数:sockfd 套接字id
backlog 允许链接的个数。
返回值:成功 0
失败 -1;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:从已经监听到的队列中取出有效的客户端链接并
接入到当前程序。
参数:sockfd 套接字id
addr 如果该值为NULL ,表示不论客户端是谁都接入。
如果要获取客户端信息,则事先定义变量
并传入变量地址,函数执行完毕将会将客户端
信息存储到该变量中。
addrlen: 参数2的长度,如果参数2为NULL,则该值
也为NULL;
如果参数不是NULL,&len;
一定要写成len = sizeof(struct sockaddr);
返回值:成功 返回一个用于通信的新套接字id;
从该代码之后所有通信都基于该id
失败 -1;
接受函数:/发送函数:
read()/write () ///通用文件读写,可以操作套接字。
recv(,0) /send(,0) ///TCP 常用套机字读写
recvfrom()/sendto() ///UDP 常用套接字读写
ssize_t recv(int sockfd, void *buf, size_t len,int flags);
功能:从指定的sockfd套接字中以flags方式获取长度
为len字节的数据到指定的buff内存中。
参数:sockfd
如果服务器则是accept的返回值的新fd
如果客户端则是socket的返回值旧fd
buff 用来存储数据的本地内存,一般是数组或者
动态内存。
len 要获取的数据长度
flags 获取数据的方式,0 表示阻塞接受。
返回值:成功 表示接受的数据长度,一般小于等于len
失败 -1;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:该函数固定有客户端使用,表示从当前主机向目标
主机发起链接请求。
参数:sockfd 本地socket创建的套接子id
addr 远程目标主机的地址信息。
addrlen: 参数2的长度。
返回值:成功 0
失败 -1;
int send(int sockfd, const void *msg, size_t len, int flags);
功能:从msg所在的内存中获取长度为len的数据以flags
方式写入到sockfd对应的套接字中。
参数:sockfd:
如果是服务器则是accept的返回值新fd
如果是客户端则是sockfd的返回值旧fd
msg 要发送的消息
len 要发送的消息长度
flags 消息的发送方式。
返回值:成功 发送的字符长度
失败 -1;
TCP与UDP的区别
TCP
1.有链接:在发送消息前,会先打通一条链路,在一次会话中,链路不会改变(三次握手,四次挥手),客户端和服务端可以检测对方是否关闭,当一端关闭时,另一端可以知道
2.可靠传输:应答机制,自动重传
3.流式套接字,数据连续且有顺序,数据间无边界,会有粘包问题
解决:1.设置边界,2.固定大小,3.自定义协议
4.全双工通信:有两个缓冲区,每个大小64K,缓冲区满会阻塞,收发互不影响
UDP
1.无连接:客户端与服务端无法检测对方是否关闭,需要手动关闭,我写过一个基于UDP的两端聊天功能,由于另一端无法自动检测,所以我在一端关闭前向另一端发送一个结束标志,来通知另一端。
2.不可靠,数据会有丢包风险
3.数据报 发送和接收次数必须对应,数据之间有边界
4.无拥塞控制 不会管网络是否拥挤,会继续发送,大大增加丢包率
5.可以组播,广播(仅限UDP)
6.首部开销小