网络编程(三)
网络编程(三)
- 基于UDP的网络客户端和服务端模型
- 服务端
- **socket**:创建网络软通道
- **bind**:给socket套接字绑定网络终端主机信息
- **recvfrom**:接收数据报通道中的数据
- **sendto**:发数据
- 客户端
- TCP与UDP的网络通信模型区别
基于UDP的网络客户端和服务端模型
服务端
- socket
- bind
- IO函数(recvfrom/sendto)
socket:创建网络软通道
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//参数1:协议域(AF_INET)ipv4
//参数2:套接字类型–>SOCK_DGRAM 数据报套接字
//参数3:其他协议–>0:自动匹配其他需要的协议
//返回值:成功返回文件描述符,标识socket网络软通道;失败返回-1,更新errno
bind:给socket套接字绑定网络终端主机信息
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//参数1:文件描述符–>socket返回值
//参数2:指向网络终端主机信息的结构体(协议域,IP地址,端口号)
//参数3:struct sockaddr的大小
//返回值:成功返回0,失败返回-1,更新errno
//原结构体
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
//替换的结构体
struct sockaddr_in{
sa_family_t sin_family;
in_port_t sin_port;
struct in_adrr sin_addr;
};
struct in_addr{
__bs32 s_addr;
}
该结构体总共占16字节,两个结构体可以强转的前提是所占空间大小相同,借用struct sockaddr_in结构体存储,之后强转为struct sockaddr
recvfrom:接收数据报通道中的数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
//参数1:文件描述符–>socket返回值
//参数2:存放接收数据的BUF
//参数3:接收数据大小
//参数4:阻塞&非阻塞的标志
//参数5:用来存放对方主机的相关信息(客户端地址)
//参数6:struct sockaddr 大小的指针
//返回值:成功返回接收到字节个数;失败返回-1,并更新errno
sendto:发数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
//参数1:文件描述符–>socket返回值
//参数2:存放发送数据的BUF
//参数3:接收数据大小
//参数4:阻塞&非阻塞的标志
//参数5:用来存放对方主机的相关信息(客户端地址)
//参数6:struct sockaddr 大小
//返回值:成功返回发送的字节个数;失败返回-1,并更新errno
server:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUF_SIZE 20//udpserver
int main()
{//socketint serverfd = socket(AF_INET, SOCK_DGRAM, 0);//判断socket返回值if(-1 == serverfd){perror("socket error");return -1;}//创建软通道成功printf("socket ok----\r\n");//bindstruct sockaddr_in stserver;stserver.sin_family = AF_INET;stserver.sin_port = htons(6666);stserver.sin_addr.s_addr = inet_addr("127.0.0.1");int ret = bind(serverfd, (struct sockaddr*)&stserver, sizeof(struct sockaddr));//判断的返回值if(-1 == ret){//失败打印失败原因并返回perror("bind error");return -1;}//绑定服务端主机成功printf("bind ok-----\r\n");//接受客户端的信息struct sockaddr_in stclient;//结构体大小socklen_t len = sizeof(struct sockaddr);//创建收发数据的缓冲区char buf[BUF_SIZE] = {0};while(1){//清空接收数据的缓冲区memset(buf, 0, BUF_SIZE);//接收客户端的信息ret = recvfrom(serverfd, buf, BUF_SIZE, 0, (struct sockaddr*)&stclient, &len);//判断返回值if(ret <= 0){//接收数据失败或者没有数据,不退出,直接进行下一次客户端连接perror("recvfrom error or recvfrom end");continue;}//打印输出接收到实际的数据printf("recvfrom data:%s\r\n", buf);//清空缓冲区,为发送数据做准备memset(buf, 0, BUF_SIZE);printf("please write:\r\n");//从标准输入输入数据fgets(buf, BUF_SIZE, stdin);//发送数据ret = sendto(serverfd, buf, BUF_SIZE, 0, (struct sockaddr*)&stclient, len);//返回值判断if(ret <= 0){//发送完毕或者发送失败,不退出,进行下一次客户端连接perror("sendto error or sendto end");continue;}}return 0;
}
客户端
- socket
- IO函数(sendto/recvfrom)
client:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//udpclient
#define BUF_SIZE 20
int main()
{//socketint clientfd = socket(AF_INET, SOCK_DGRAM, 0);//判断socket返回值if(-1 == clientfd){//返回值为-1,代表创建软通道出错,打印出错原因并返回perror("socket error \r\n");return -1;}//sendtostruct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(6666);serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");socklen_t len = sizeof(struct sockaddr);//接收数据的缓冲区char buf[BUF_SIZE] = {0};printf("please write:\r\n");//从标准输入接收数据fgets(buf, BUF_SIZE, stdin);//发送数据int ret = sendto(clientfd, buf, BUF_SIZE,0,(struct sockaddr *)&serveraddr, len);if(-1 == ret){//失败发送perror("sendto error");}//recvfrommemset(buf, 0, BUF_SIZE);//接收客户端的信息ret = recvfrom(clientfd, buf, BUF_SIZE,0,(struct sockaddr *)&serveraddr, &len);//返回值判断if(-1 == ret){//接收失败,返回并关闭数据报套接字perror("recvfrom error");close(clientfd);return -1;}//否则,打印接收的数据,并关闭数据报套接字printf("recvfrom:%s\r\n", buf);close(clientfd);return 0;
}
TCP与UDP的网络通信模型区别
常见套接字类型:
-
流式套接字(SOCK_STREAM) 提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设 置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
-
数据报套接字(SOCK_DGRAM) 提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复, 顺序发送,可能乱序接收。
-
原始套接字(SOCK_RAW)–ping.c 可以对较低层次协议如IP、ICMP
-
直接访问 unix域套接字–》本地通信
tcp:面向连接的可靠的传输协议,通过三次握手保证可靠传送。
udp:面向无连接的不可靠的传输协议,没有三次握手,可以采用数据重传。
TCP(传输控制协议):
- TCP是一个可靠的,全双工的,有序的,面向链接的字节流通信的协议。
- 为什么可靠:
- 丢失的数据包重发 (能保证拿到数据)
- 错误的数据包重发 (保证能拿到正确的数据)
- 数据的有序到达(因为对每个数据包进行了编号)(拆包,编号)
- 有较为健全的校验机制(为了保证数据的正确性)
- 支持面向连接(保证通信线路的畅通)–>三次握手
- 有信道拥堵控制(通过一种对于信道拥堵解决的方案,来提高转发效率)产生的原因,是中继设备中(接 收的速度 >> 发送的速度)
- 为什么有序(有序列号):
- 保证数据都能传输给对端,不至于当传输的数据 > 信道带宽 ,导致数据丢弃。
- 通过序号,在对端主机上可以拼接成原本的数据包
- 保证数据传输的可靠性
- 如何面向链接: 三次握手和四次挥手
UDP(The User Datagram Protocol):
无连接的数据报协议,别名"不可靠的协议"
- 使用校验和来实现错误侦测
- UDP常用于媒体流的传输(音频、视频、等),在这种情况下,实时性比可靠性更重要
- UDP也常用于简单的查询/回应程序,例如DNS查找,在这种情况下,建立可靠传输的资源消耗太大
- UDP是一种实时传输协议(Real-time Transport Protocol),这种协议通常用来传输实时数据例如:音 视频流
- 不可靠的原因:
- 非面向连接(不关心接收端是否在线)–》没有三次握手
- 丢包不重发
- 错误的包不重发
- 没有信道拥堵控制
- 有一个最大传输长度限制
- 没有严格的校验机制
- 如何抉择使用TCP还是UDP
- 可靠性
- 实时性
- 可靠性 > 实时性: TCP 可靠性 < 实时性: UDP