目录
TCP和UDP通信各自的优缺点:
TCP:面向连接的,可靠数据包传输
对于不稳定的网络层,采取完全弥补的通信方式。丢包重传。
优点:稳定。--- 数据流量稳定、速度稳定、传输数据包的顺序稳定
缺点:传输速度慢、效率低、系统资源开销大
使用场景:数据的完整性要求较高,不追求效率、大数据传输、文件传输
UDP:无连接的,不可靠的数据报传输
对于不稳定的网络层,采取完全不弥补的通信方式。默认还原网络状况
优点:传输速度快、效率高、系统资源开销小
缺点:不稳定。 --- 数据流量、速度不稳定。尤其是传输数据包的顺序不稳定
使用场景:对时效性要求较高场合、稳定性其次。(游戏、视频会议、视频电话)
UDP随之也伴随着数据传输不可靠,传输数据的正确率、传输顺序和流量都得不到控制和保证。所以,通常情况下,使用UDP协议进行数据传输,为保证数据的正确性,我们需要在应用层添加辅助校验协议来弥补UDP的不足,以达到数据可靠传输的目的。
UDP服务器
传输层主要应用的协议模型有两种,一种是TCP协议,另外一种是UDP协议。TCP协议在网络通信中占主导地位,绝大多数的网络通信借助TCP协议完成数据传输。但UDP也是网络通信中不可或缺的重要通信手段。
相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠报文传递”。
与TCP类似的,UDP也有可能出现缓冲区被填满后,再接收数据时丢包的现象。由于它没有TCP滑动窗口的机制,通常采用如下两种方法解决:
<1>服务器应用层设计流量控制,控制发送数据速度
<2>借助setsockopt函数改变缓冲区大小。
#include<sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
//应用
int n = 220x1024;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
C/S模型 - UDP
recv()/send() 只能用于TCP通信,替代read、write
recvfrom()/sendto()用于UDP通信,替代read、write
流式套接字(SOCK_STREAM):该类套接字提供了面向连接的、可靠的、数据无错并且无重复的数据发送服务,而且发送的数据时按顺序被接受的。所有利用该套接字进行传递的数据均被视为连续的字节流且无长度限制。这对数据的稳定性、正确性和发送/接受顺序要求严格的应用十分适用,TCP协议使用该类接口,但其对线路的占用率相对比较高。流式套接字的实现屡见不鲜,如远程登录(TELNET)、文件传输协议(FTP)等使用了流式套接字。
数据报套接字(SOCK_DGRAM):数据报套接字提供了面向无连接的服务。它独立地以数据包形式发送数据(数据包长度不能大于32KB),不提供正确性检查,也不保证各数据包的发送顺序,因此可能出现数据的重发、丢失等现象,并且接受顺序由具体路由决定。然而,数据报的实现对网络线路占有率较低,NFS(网络文件系统)采用此类套接字,在TCP/IP 协议族中,UDP(UserDatagramProtocol)使用该类接口。
recvfrom()函数
该函数涵盖accept传出地址结构
#include<sys/types.h>
#include<sys/socket.h>
/*
sockfd:socket()构建的文件描述符
buf:缓冲区地址
len:缓冲区大小
flags:默认为0
src_addr:(struct sockaddr *)&addr 传出,对端地址结构
addrlen:传入传出地址大小
返回值:
成功:接收数据字节数
失败:-1
errno:0 :对端关闭
*/
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sendto()函数
#include <sys/types.h>
#include <sys/socket.h>
/*
sockfd:socket()构建的文件描述符
buf:存储数据缓冲区地址
len:数据长度
flags:默认为0
src_addr:(struct sockaddr *)&addr 传入,对端地址结构
addrlen:传入传出地址大小
返回值:
成功:成功写出数据字节数
失败:-1,errno
*/
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
Server端
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000
int main(void){
struct sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
int sockfd;
char buf[BUFSIZ];
char str[INET_ADDRSTRLEN];
int i, n;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//将地址结构清零,用于其他不同IP连接
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
printf("Accepting connections ... \n");
while(1){
clie_addr_len = sizeof(clie_addr);
//成功接收数据字节数
n = recvfrom(sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&clie_addr, &clie_addr_len);
if(n == -1){
perror("recvfrom error");
}
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
ntohs(clie_addr.sin_port));
for(i = 0;i < n; i++){
buf[i] = toupper(buf[i]);
}
n = sendto(sockfd, buf, n , 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
if(n == -1){
perror("sendto error");
}
}
close(sockfd);
return 0;
}
Client端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000 //在网路环境中唯一表示一个进程
int main(int argc, char *argv[]){
struct sockaddr_in servaddr;
int sockfd, n;
char buf[BUFSIZ];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while(fgets(buf, BUFSIZ, stdin) != NULL){
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(n == -1){
perror("sendto error");
}
//NULL:不关心对端信息
n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);
if(n == -1){
perror("recvfrom error");
}
//屏幕输出buf
write(STDOUT_FILENO, buf, n);
}
close(sockfd);
return 0;
}