8 基本UDP套接字编程

典型UDP客户端和服务端结构与代码调用顺序:


recvfrom和sendto函数:



验证接收到的响应:

对于UDP,其不像TCP是基于有连接的,这样就有一个问题,sendto之后,recvfrom回来的数据可能不是从sendto目的地址回复的消息,这样就需要验证接收到的响应,提供如下方法供参考:

  1. 比较sendto目的地址和recvfrom接收地址,查看是否一样。不过这样做有风险,如果服务器是多ip地址的,其每次回响应的地址可能不一样,所以产生如下俩方法;
  2. 客户端推荐做法:客户端通过recvfrom回来的ip地址,通过DNS来查找其域名,用域名来判断而不是用IP地址;
  3. 服务端推荐做法:服务器端,为每个IP地址创建一个socket,并通过bind绑定到对应socket种,然后加入到select中,再从可读的套接字中给出应答。

UDP的connet函数:

思考一个问题,如果服务器没启动,客户端sendto发送一个数据,结果会是什么呢?

结论是,sendto成功返回,如果客户端还同时调用了recvfrom,则将永远堵塞在recvfrom函数(当然可以设超时),同时通过tcpdump还可以看到,服务端返回icmp port unreachable错误消息,但是这个消息并没有通过sendto和recvfrom函数返回给用户进程,换句话说,用户并不知道服务端返回了icmp错误。怎么办呢,udp的connet函数可以搞定这些。

对于udp socket调用connect,称之为已连接UDP socket,其余未连接UDP socket区别如下:

  1. 不使用sendto,而使用write或send,因为在connect中,已经指定目的端IP地址;
  2. 不应用recvfrom,而使用read或recv或recvmsg,注意,如果源地址不是connect连接的目的地址,是不会回馈到该套接字的,这正好由内核帮我们完成了验证接收到的响应;
  3. 由已连接的UDP套接字引发的异步错误,会返回给他们所在的进程,这样就很好的解决了上述问题;
  4. 在调用connect,确定目的IP和port之外,同时还会通过目的地址,查找路由表,确定本地地址,connect之后调用getsockname可以获取到。

总结:UDP客户或服务进程,仅在使用自己的UDP套接字与确定的唯一对端进行通信时,才会调用connect,当然,一般UDP客户端会用connect多一点。同时如果采用connect,其性能也会得到提升,因为对于sendto来讲,其发送数据前和后,需要连接套接字、断开套接字,如果connect之后,这两步就省下了。

udp的流量控制
通过sendto函数,其返回值是会返回一个已发送字节数,但是这个仅仅表示用户进程已经写入到发送缓冲区的数据,并不是真正发送给目的端的字节数,所以如何控制udp的发送速度是一个值得考虑的问题。

例子:

下面给出 已连接UDP套接字的客户端代码通过采用select的TCP和UDP混合的服务端代码

udp client:

/* sockclnt.c*/

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h> /*for struct sockaddr_in*/





#define SVR_IP      "127.0.0.1"

#define SVR_PORT    1234

#define BUFSIZE	    255





int main()

{

	int ret     = 0;

	int sockfd  = 0;

    struct sockaddr_in svr_addr;

    memset(&svr_addr, 0, sizeof(struct sockaddr_in));



    // create client socket fd

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	if (sockfd == -1) {

		perror("socket failed");

		exit(1);

	}



    // connet to server

	svr_addr.sin_family         = AF_INET;                 

	svr_addr.sin_port           = htons(SVR_PORT);          

	svr_addr.sin_addr.s_addr    = inet_addr(SVR_IP); 

    ret = connect(sockfd, (struct sockaddr *)&svr_addr, sizeof(struct sockaddr_in));

	if (ret == -1) {

		perror("connect failed");

		exit(1);

	}

    

    int i = 0;

    for (i = 0; i < 150; i++)

    {

        char * msg = "udp hello server....";

        printf("client send msg: %s\n", msg);

		if((send(sockfd, msg,	strlen(msg), 0))<0)

        {

			perror("send()");

        }



        char recv_buf[BUFSIZE] = {0};

        int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf), 0);

        printf("client recv msg: %s%d\n", recv_buf, recv_len);



        sleep(1);

    }   

	

    close(sockfd);

}



tcp client:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h> /*for struct sockaddr_in*/





#define SVR_IP      "127.0.0.1"

#define SVR_PORT    1234

#define BUFSIZE	    255





int main()

{

	int ret     = 0;

	int sockfd  = 0;

    struct sockaddr_in svr_addr;

    memset(&svr_addr, 0, sizeof(struct sockaddr_in));



    // create client socket fd

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (sockfd == -1) {

		perror("socket failed");

		exit(1);

	}



    // connet to server

	svr_addr.sin_family         = AF_INET;                 

	svr_addr.sin_port           = htons(SVR_PORT);          

	svr_addr.sin_addr.s_addr    = inet_addr(SVR_IP); 

    ret = connect(sockfd, (struct sockaddr *)&svr_addr, sizeof(struct sockaddr_in));

	if (ret == -1) {

		perror("connect failed");

		exit(1);

	}

    

    int i = 0;

    for (i = 0; i < 100; i++)

    {

        char * msg = "tcp hello server....";

        printf("client send msg: %s\n", msg);

		if((send(sockfd, msg,	strlen(msg), 0))<0)

        {

			perror("send()");

        }



        char recv_buf[BUFSIZE] = {0};

        int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf), 0);

        printf("client recv msg: %s%d\n", recv_buf, recv_len);



        sleep(2);

    }   

	

    close(sockfd);

}




udp tcp select server

/******************************************************************************



  Copyright (C), 2001-2011, DCN Co., Ltd.



 ******************************************************************************

  File Name     : main.c

  Version       : Initial Draft

  Author        : Dong Shen

  Created       : 2012/9/19

  Last Modified :

  Description   : transcode main

******************************************************************************/





#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <sys/queue.h>



#define MYPORT          1234    // the port users will be connecting to

#define BACKLOG         512     // how many pending connections queue will hold

#define BUF_SIZE        1024



#define MAX_PATH_LEN    255



#ifndef bool

    #define bool int

#endif



#define FALSE 0

#define TRUE  1



typedef struct fd_a_t

{

    int     fd_a;    

    bool    need_write;



    char *  ret_buf;

    int     ret_buf_len;

    TAILQ_ENTRY(fd_a_t) fd_a_node;

}fd_a_t;



typedef  TAILQ_HEAD(fd_a_list_t_, fd_a_t) fd_a_list_t;





void free_node(fd_a_t * cur_fd_node)

{

    if (NULL == cur_fd_node)

    {

        return;

    }

    

    if (cur_fd_node->ret_buf != NULL)

    {

        free(cur_fd_node->ret_buf);

        cur_fd_node->ret_buf = NULL;

    }



    

    free(cur_fd_node);

    cur_fd_node = NULL;



    return;

}



int main(void)

{    

    int ret         = 0;

    int listen_fd   = 0; // listen on listen_fd

    int new_fd      = 0; // new connection on new_fd

    int udp_fd      = 0; // udp socket fd

    struct sockaddr_in server_addr; // server address information

    struct sockaddr_in client_addr; // connector's address information

    memset(&server_addr, 0, sizeof(struct sockaddr_in));

    memset(&client_addr, 0, sizeof(struct sockaddr_in));

    

    // create tcp listen socket, bind, listen

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);

    if (-1 == listen_fd) 

    {

        perror("socket");

        return -1;

    }

  

    int yes = 1;  

    ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));  

    if (-1 == ret)   

    {  

        perror("setsockopt");  

        return -1;  

    }  



    server_addr.sin_family      = AF_INET;        // host byte order

    server_addr.sin_port        = htons(MYPORT);  // short, network byte order

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);     // automatically fill with my IP

    memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));



    ret = bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));

    if (-1 == ret) 

    {

        perror("bind");

        return -1;

    }



    ret = listen(listen_fd, BACKLOG);

    if (-1 == ret)

    {

        perror("listen");

        return -1;

    }



    udp_fd = socket(AF_INET, SOCK_DGRAM, 0);

    if (-1 == udp_fd) 

    {

        perror("socket");

        return -1;

    }

  

    bzero(&server_addr, sizeof(struct sockaddr_in));

    server_addr.sin_family      = AF_INET;        // host byte order

    server_addr.sin_port        = htons(MYPORT);  // short, network byte order

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);     // automatically fill with my IP

    memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));



    ret = bind(udp_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));

    if (-1 == ret) 

    {

        perror("bind");

        return -1;

    }





    int maxsock     = 0;

    int conn_amount = 0;

    fd_a_list_t  fd_a_hd;

    TAILQ_INIT(&fd_a_hd);

    

    fd_set read_fdsr;

    fd_set write_fdsr;

         

    // timeout setting    

    // struct timeval tv;    

        

    while (1) 

    {

        // tv.tv_sec = 30;

        // tv.tv_usec = 0;

        

        // initialize file descriptor set    

        FD_ZERO(&read_fdsr);

        FD_ZERO(&write_fdsr);

        FD_SET(listen_fd, &read_fdsr);

        FD_SET(udp_fd, &read_fdsr);

        maxsock = listen_fd > udp_fd ? listen_fd : udp_fd;



        fd_a_t * cur_fd_node = TAILQ_FIRST(&fd_a_hd);

        while (cur_fd_node != NULL)

        {

            FD_SET(cur_fd_node->fd_a, &read_fdsr);

            FD_SET(cur_fd_node->fd_a, &write_fdsr);

            if (cur_fd_node->fd_a > maxsock)

            {

                maxsock = cur_fd_node->fd_a;

            }

            cur_fd_node = TAILQ_NEXT(cur_fd_node, fd_a_node);

        }



        // select read write socket fd, read_fdsr.

        ret = select(maxsock + 1, &read_fdsr, &write_fdsr, NULL, NULL);

        if (ret < 0) 

        {

            perror("select");

            break;

        } 

        else if (ret == 0) 

        {

            printf("select timeout\n");

            continue;

        }

        

        // check whether a new connection comes

        if (FD_ISSET(listen_fd, &read_fdsr)) 

        {

            int sin_size = sizeof(client_addr);

            new_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &sin_size);

            if (new_fd <= 0) 

            {

                perror("accept");

                continue;

            }



            // add to fd queue

            if (conn_amount < BACKLOG) 

            {

                // create new fd node, insert into fd list

                fd_a_t * new_fd_a_node  = calloc(1, sizeof(fd_a_t));

                new_fd_a_node->fd_a     = new_fd;

                TAILQ_INSERT_TAIL(&fd_a_hd, new_fd_a_node, fd_a_node);

                conn_amount++;

                

                printf("new connection client[%d] %s:%d\n", new_fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));        

            }

            else 

            {

                printf("max connections arrive, exit\n");

                send(new_fd, "bye", 4, 0);

                close(new_fd);

                continue;

            }

        }



        if (FD_ISSET(udp_fd, &read_fdsr)) 

        {

            char msg[BUF_SIZE] = {0};

            struct sockaddr_in cliaddr;

            socklen_t len = sizeof(struct sockaddr_in);

            ssize_t n = recvfrom(udp_fd, msg, BUF_SIZE, 0, (struct sockaddr *)&cliaddr, &len);

            printf("udp recv: %s\n", msg);

            printf("udp send: %s\n", msg);

            sendto(udp_fd, msg, n, 0, (struct sockaddr *)&cliaddr, len);

        }

        // check every fd in the set

        cur_fd_node = TAILQ_FIRST(&fd_a_hd);

        while(cur_fd_node != NULL)

        {

            if (FD_ISSET(cur_fd_node->fd_a, &read_fdsr))

            {

                char recv_buf[BUF_SIZE] = {0};

                int ret_len = recv(cur_fd_node->fd_a, recv_buf, sizeof(recv_buf), 0);

                if (ret_len <= 0) // client close

                {        

                    printf("client[%d] close\n", cur_fd_node->fd_a);

                          

                    // close socket, clear fd_set

                    close(cur_fd_node->fd_a);

                    

                    // remove fd_node from fd_list

                    fd_a_t * next_fd_node = TAILQ_NEXT(cur_fd_node, fd_a_node);

                    TAILQ_REMOVE(&fd_a_hd, cur_fd_node, fd_a_node);



                    free_node(cur_fd_node);

                    

                    cur_fd_node = next_fd_node;

                    conn_amount--;



                    continue;

                } 

                else // receive data

                {        

                    printf("tcp recv: %s\n", recv_buf);

                    cur_fd_node->need_write  = TRUE;

                    cur_fd_node->ret_buf_len = ret_len;

                    cur_fd_node->ret_buf     = calloc(1, strlen(recv_buf) + 1);

                    strcpy(cur_fd_node->ret_buf, recv_buf);

                }

            }



            if (FD_ISSET(cur_fd_node->fd_a, &write_fdsr))

            {

                if (TRUE == cur_fd_node->need_write)

                {            

                    printf("tcp send %s\n", cur_fd_node->ret_buf);



                    send(cur_fd_node->fd_a, cur_fd_node->ret_buf, cur_fd_node->ret_buf_len, 0);

                    cur_fd_node->need_write = FALSE;

                }

            }

            cur_fd_node = TAILQ_NEXT(cur_fd_node, fd_a_node);

        }

    }



    fd_a_t * cur_fd_node = NULL;

    while ((cur_fd_node = TAILQ_FIRST(&fd_a_hd)) != NULL)

    {

        printf("client[%d] close\n", cur_fd_node->fd_a);

        

        // close socket, clear fd_set

        close(cur_fd_node->fd_a);

        

        // remove fd_node from fd_list

        TAILQ_REMOVE(&fd_a_hd, cur_fd_node, fd_a_node);



        free_node(cur_fd_node);

        

        conn_amount--;        

    }

   

    return 1;

}








  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值