UDP协议
客户端
采用recv()、recvfrom()、send()、sendto()函数与服务器进行通信
注意:以上函数都是阻塞性函数
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <arpa/inet.h>
int sockfd;
int main(int argc, char *argv[])
{
if(argc < 3){
printf("usage: %s port\n", argv[0]);
exit(1);
}
//步骤1:创建套接字socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);//基于UDP用SOCK_DGRAM
if(sockfd < 0){
perror("socket error\n");
exit(1);
}
//步骤2:调用recvfrom和sendto等函数和服务器双向通信
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET; //ipv4
serveraddr.sin_port = htons(atoi(argv[2])); //port
inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);//ip
char buffer[1024] = "hello, This is client";
if(sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){
perror("sendto error");
exit(1);
}
else{
//接受服务器端发送的数据报文
memset(buffer, 0, sizeof(buffer));
size_t size;
if((size = recv(sockfd, buffer, sizeof(buffer), 0)) < 0){//因为udp是无连接协议,所以不需要判断是否=0
//recvfrom()还要多发送方地址信息的2个参数,这里已经成功发送,所以recv即可
perror("recv error");
exit(1);
}
else{
printf("%s", buffer);
}
}
close(sockfd);
return 0;
}
结果
1. 也可以用send函数代替sento函数:
如果用send也可以发送信息,但在此之前要调用connect进行连接
if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){
perror("connect error");
exit(1);
}
TCP中调用connect是三次握手,而在UDP中只是在内核中记录了服务器的ip、端口
if(send(sockfd, buffer, sizeof(buffer), 0) < 0){
perror("send error");
exit(1);
}
如果不用connect,可能会接收到来自其他ip发来的信息~
2. 阻塞性函数有其缺陷
服务器刚打开,就会阻塞在recvfrom()中
如果用UDP进行传送,发送数据报文丢失,服务器就会一直阻塞在这里;客户端的收发也一样。
解决办法:
(1)超时控制,避免进程陷入无限期等待
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
参数说明:要设定的定时时间,以秒为单位。在alarm调用成功后开始计时。超过该时间将触发SIGALRM信号
返回值:返回当前进程曾经设置的定时器剩余秒数。
(2)在套接字选项中设置超时控制
//设置套接字选项:使调用之前停掉的端口马上可以使用
int ret;
int opt = 1;
if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0){
perror("setsockopt error");
exit(1);
}
应用程序通过创建一个linger结构来设置相应的操作特性:
struct linger {
int l_onoff;
int l_linger;
};
为了允许SO_LINGER,应用程序应将l_onoff设为非零,将l_linger设为零或需要的超时值(以秒为单位),然后调用setsockopt()。为了允许SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff应设为零,然后调用setsockopt()。