UDP客户/服务器

《UNIX Network Programming Volume1: The Socket Networking API, Third Edition》
W.Richard Stevens / Bill Fenner / Andrew M.Rudoff

  • 一般来说,大多数TCP服务器是并发的,而大多数UDP服务器是迭代的(单个进程/线程就得处理所有客户)。
  • UDP套接字调用connect(不同于TCP):没有三路握手过程,内核只是检查是否存在错误,记录对端IP地址和端口号。

    • 未连接UDP套接字(unconnected UDP socket):新创建的UDP套接字默认如此。
    • 已连接UDP套接字(connected UDP socket):对创建的UDP套接字调用connect的结果。
  • 未连接UDP套接字常使用recvfromsendto,已连接UDP套接字常使用readwrite

  • 一个已连接UDP套接字能且仅能与一个对端IP地址交换数据报。
  • 已连接UDP套接字引发的异步错误会返回给它们的进程,而未连接UDP套接字不接收任何异步错误。
  • 对于TCP套接字,connect只能调用一次。对于UDP,再次调用connect可以指定新的IP地址和端口号或者断开套接字(把地址族设为AF_UNSPEC)。
  • TCP端口与UDP端口相互独立。
  • UDP缺乏流量控制,如果套接字缓冲区时会丢弃数据。

recvfrom和sendto

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t *addrlen);

前三个参数:等同于read和write函数,而且sendto长度为0的数据报或者recvfrom返回0值都是可行的。
flag参数:与recv/send类型函数相关。
sendto的后两个参数:指向一个含有数据报接收者的协议地址(类似于connect)。
recvfrom的后两个参数:指向一个数据报发送者的协议地址(类似于accept)。
返回值:所接收数据报中的用户数据量大小。

未连接UDP套接字客户/迭代服务器

使用UDP重写上篇文章” TCP客户/服务器“中的回射服务例子如下:
udpcli.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define SERV_PORT 8755
#define MAXLINE 32
#define error_exit(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

void dg_cli(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {
    int n;
    char sendline[MAXLINE], recvline[MAXLINE+1], buf[MAXLINE];
    socklen_t len;
    struct sockaddr *preply_addr;

    preply_addr = malloc(servlen);
    while (fgets(sendline, MAXLINE, fp) != NULL) {
        sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

        len = servlen;
        n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
        printf("reply from %s:%d : ", 
            inet_ntop(AF_INET, &((struct sockaddr_in *)preply_addr)->sin_addr, buf, sizeof(buf)),
            ntohs(((struct sockaddr_in *)preply_addr)->sin_port));      
        if (len != servlen || memcmp(pservaddr, preply_addr, len) != 0) 
            printf("(ignored)");

        recvline[n] = 0;
        fputs(recvline, stdout);
    }
    free(preply_addr);
}       

int main(int argc, char **argv) {
    int sockfd;
    struct sockaddr_in servaddr;

    if (argc != 2)
        error_exit("usage: udpcli <IPaddress>");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    dg_cli(stdin, sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    exit(0);
}

udpserv.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define SERV_PORT 8755
#define MAXLINE 32
#define error_exit(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)   

void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {
    
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值