学习嵌入式的第三十二天——网络编程——TCP

发布于:2025-09-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

服务端/客户端模型

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.首部开销小


网站公告

今日签到

点亮在社区的每一天
去签到