服务器:
一、创建套接字描述符socket
二、设置服务器的IP地址和端口号(网络字节序)
三、套接字描述符绑定到服务器地址bind
四、从套接字描述符读取来自客户端的请求并取得客户端的地址recvfrom
五、向套接字描述符写入应答并发送给客户端sendto
六、回到步骤四等待读取下一个客户端的请求
客户端:
一、创建套接字描述符socket
二、设置服务器的IP地址和端口号(网络字节序)
三、向套接字描述符写入请求并发送给服务器sendto
四、从套接字描述符读取来自服务器的应答recvfrom
五、关闭套接字描述符
用于有连接及无连接的socket上发送数据
#include
#include
ssize_t sendto(int socket, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
成功返回发送字节数,失败返回-1,errno查看错误码
用于有连接及无连接的socket上接收数据
#include
#include
ssize_t recvfrom(int socket, const void *buf, size_t len, int flags, const struct sockaddr *src_addr, socklen_t addrlen);
成功返回接收字节数,被关闭返回0,失败返回-1,errno查看错误码
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(struct sockaddr_in));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons(9999);
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket() failed");
return -1;
}
char on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
if (ret < 0)
{
perror("bind() failed");
return -1;
}
puts("waiting data");
struct sockaddr_in raddr;
int val = sizeof(struct sockaddr);
char rbuf[50];
ret = recvfrom(sockfd, rbuf, 50, 0, (struct sockaddr *)&raddr, (socklen_t *)&val);
if (ret < 0)
perror("recvfrom failed");
printf("recv data: %s\n", rbuf);
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(struct sockaddr_in));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("failed socket");
return -1;
}
char on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
puts("please enter data:");
char wbuf[50] = "write to udpserver";
//scanf("%s", wbuf, sizeof(wbuf));
//sscanf(wbuf, "%s", "write to udpserver");
int ret = sendto(sockfd, wbuf, sizeof(wbuf), 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
if (ret < 0)
perror("sendto failed");
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(struct sockaddr_in));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
puts("socket failed");
return -1;
}
char on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
if (ret < 0)
{
puts("sbind failed");
return -1;
}
struct sockaddr_in raddr;
int val = sizeof(struct sockaddr);
char rbuf[50];
while (1)
{
puts("waiting data");
memset(rbuf, 0, 50);
ret = recvfrom(sockfd, rbuf, 50, 0, (struct sockaddr *)&raddr, (socklen_t *)&val);
if (ret == 0){
printf("recvfrom empty\n");
break;
}
if (ret < 0)
perror("recvfrom failed");
printf("recv data: %s\n", rbuf);
}
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(struct sockaddr_in));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("failed socket");
return -1;
}
char on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
int ret;
char wbuf[50] = "write to udpserver";
for (int i=0; i<5; i++)
{
puts("please enter data:");
//memset(wbuf, 0, sizeof(wbuf));
//scanf("%s", wbuf, sizeof(wbuf));
ret = sendto(sockfd, wbuf, sizeof(wbuf), 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
if (ret < 0)
perror("sendto failed");
}
ret = sendto(sockfd, NULL, 0, 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
if (ret == 0)
printf("sendto empty\n");
close(sockfd);
return 0;
}
UDP是无连接、面向消息的数据传输协议,数据报容易丢失,数据报无序。
丢包通常原因是服务器端socket接收缓存满了(UDP无流量控制,发送速度快于接收速度,容易出现这种情况),就会丢弃后面接收的包,且收到包后处理这段时间不会去接收包。可单独开线程去接收UDP数据。
要实现数据的可靠传输,必须实现流控制、超时和重传机制。
简单丢包处理:数据分块,加上ID,丢包ID发回服务器端,重新发送。
常见可靠传输算法有模拟TCP和重发请求(ARQ)协议(连续ARQ协议、选择重发ARQ协议、滑动窗口协议)。