UDP的特点
--无连接
--基于消息的数据传输服务
--不可靠
--UDP更加高效
UDP注意点
--UDP报文可能会丢失,重复
--UDP报文可能会乱序
--UDP缺乏流量控制(UDP缓冲区写满之后,没有流量控制,会覆盖缓冲区)
--UDP协议数据报文截断(如果接收到的数据报文大于缓冲区,报文可以被截断,后面部分丢失)
--recvfrom返回0,不代表连接关闭(实际上UDP是无连接的),sendto可以发送数据0包,只含有UDP头部
--UDP客户端也可以调用connect,但是并没有完成三次握手,只是维护了一个状态信息(和对等方的)
--一旦调用了connect,就可以使用send函数
//UDP协议客户端
#define SETNO 0 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <termios.h> /**
* clear_back - 退格不回显
* 成功返回0,失败返回-1
* */
int clear_back()
{
struct termios term;
memset(&term, , sizeof(term));
//获取当前系统设置
if (tcgetattr(STDIN_FILENO, &term) == -)
{
perror("tcgetattr() err");
return -;
}
//修改系统设置
term.c_cc[VERASE] = '\b';
//立即生效
if (tcsetattr(STDIN_FILENO, TCSANOW, &term) == -)
{
perror("tcgetattr() err");
return -;
}
return ;
} int main(int arg, char *args[])
{
if(clear_back()!=)
return -;
int sockfd = socket(AF_INET, SOCK_DGRAM, );
if (sockfd == -)
{
perror("socket() err");
return -;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
//连接的服务器地址
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
#if SETNO==0
/*
* UDP客户端也可以调用connect,但是并没有完成三次握手,
* 只是维护了一个状态信息(和对等方的),所以发送报文时,不需要传服务器ip*/
if(connect(sockfd,(struct sockaddr *)&addr,sizeof(addr))==-)
{
perror("connect() err");
return -;
}
#endif
char buf[] = { };
int rc = ;
while (fgets(buf, , stdin) != NULL)
{
#if SETNO==0
rc = send(sockfd, buf,strlen(buf),);
#else
rc = sendto(sockfd, buf, strlen(buf), , (struct sockaddr *) &addr,
sizeof(addr));
#endif if (rc == -)
{
perror("sendto() err");
break;
}
memset(buf, , );
rc = recvfrom(sockfd, buf, , , NULL, NULL);
if (rc == -)
{
perror("recvfrom() err");
break;
}
fputs(buf, stdout);
memset(buf, , );
}
return ;
}
//UDP协议服务器端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> int main(int arg, char *args[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, );
if (sockfd == -)
{
perror("socket() err");
return -;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
//绑定本地IP地址
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -)
{
perror("bind() err");
return -;
}
int rc = ;
char buf[] = { };
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
while ()
{
memset(buf,,sizeof(buf));
do
{
rc = recvfrom(sockfd, buf, , , (struct sockaddr *) &peeraddr,
&peerlen);
} while (rc == - && errno == EINTR);
if (rc == -)
{
perror("recvfrom() err");
break;
}
//fputs()函数必须以'\n'结尾才会打印在终端上,,否则需要清空缓冲区
fputs(buf, stdout);
fflush(stdout);
//发送数据
rc = sendto(sockfd, buf, rc, , (struct sockaddr *) &peeraddr,
sizeof(peeraddr));
if (rc == -)
{
perror("sendto() err");
break;
}
}
return ;
}
.SUFFIXES:.c .o
CC=gcc
SRCS=hello.c
OBJS=$(SRCS:.c=.o)
EXEC=ser
SRCS1=tec01.c
OBJS1=$(SRCS1:.c=.o)
EXEC1=clt start:$(OBJS) $(OBJS1)
$(CC) -o $(EXEC) $(OBJS)
$(CC) -o $(EXEC1) $(OBJS1)
@echo "^_^-----OK------^_^"
.c.o:
$(CC) -Wall -g -o $@ -c $<
clean:
rm -f $(OBJS)
rm -f $(EXEC)