同一端口监听tcp和udp请求

29 篇文章 0 订阅

问题:

众所周知,同一台机器的同一个端口只可以被一个进程使用,一般用于tcp,或者udp。那一个进程使用同一个端口同时监听tcp、udp请求,是否可以呢?答案:可以。

代码:

server

为了同时监听,server使用select进行多路访问控制。
server端代码如下:

/*
TCP
INET
use select
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>


#define LENGTH_OF_LISTEN_QUEUE  20
#define SERVER_PORT 8888
#define MAXLINE 4096
#define MAX_FD_NUM  10


static int init_new_client(int client_fd);
static int remove_client(int client_fd);
static int get_max_fd(int fd);


static int  client_fdset[MAX_FD_NUM];

int main(int argc, char** argv)
{
    int    tcp_fd, udp_fd, connfd, client_fd;
    struct sockaddr_in  servaddr;
    struct sockaddr_in  client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char    recv_buff[4096]; 
    char    response[] = "recv well done.";
    int     recv_len;
    fd_set readfd;
    int ret;
    int max_fd;
    int i;

    for (i = 0; i < MAX_FD_NUM; i++) {
        client_fdset[i] = -1;
    }

    if ((tcp_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
            printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
            exit(0);
    }


    if ((udp_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ) {
            printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
            exit(0);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);

    if (bind(tcp_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
            printf("tcp bind socket error: %s(errno: %d)\n",strerror(errno),errno);
            exit(0);
    }

    if (bind(udp_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
            printf("udp bind socket error: %s(errno: %d)\n",strerror(errno),errno);
            exit(0);
    }

    if (listen(tcp_fd, LENGTH_OF_LISTEN_QUEUE) == -1) {
            printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
            exit(0);
        }

    printf("----------waiting for client's request---------\n");
    while (1) {

            /*每次调用select前,都要重置监听的描述符*/  
        FD_ZERO(&readfd);
        FD_SET(tcp_fd, &readfd);
        FD_SET(udp_fd, &readfd);
        for (i = 0; i < MAX_FD_NUM; i++) {
            if (client_fdset[i] != -1) {
                FD_SET(client_fdset[i], &readfd);
            }   
        }

        int fd = tcp_fd;
        if (udp_fd > tcp_fd)
        {
            fd = udp_fd;
        }

        max_fd = get_max_fd(fd);
        ret = select(max_fd + 1, &readfd, NULL, NULL, NULL);//有数据时才返回
            if (ret == -1) {  //错误情况
            if (errno == EINTR) {//signal interrupt
                continue;
            }
            else {
                perror("select error");
                exit(0);
            }
        }
            else if (ret) {    //返回值大于0 有数据到来
                    if (FD_ISSET(tcp_fd, &readfd))
                    {
                if((connfd = accept(tcp_fd, (struct sockaddr *)&client_addr, &client_addr_len)) == -1) {
                        printf("accept socket error: %s(errno: %d)\n",strerror(errno),errno);
                        continue;
                    }
                printf("accept a new  connection from client IP: %s, port: %u\n", inet_ntoa(client_addr.sin_addr),                      ntohs(client_addr.sin_port));
                ret = init_new_client(connfd);
                if (ret != 0) {
                    printf("init_new_client() failed ret = %d.\n", ret);
                    close(connfd);
                }

                    } else if (FD_ISSET(udp_fd, &readfd)) {

                int ret =recvfrom(udp_fd,recv_buff, MAXLINE - 1, 0,(struct sockaddr *)&client_addr, &client_addr_len);
                if(ret<0){
                    perror("recvfrom error_1");
                    continue;
                }

                    recv_buff[ret] = '\0';
                printf("udp recv:");
                    printf("recv msg %d byte from client: %s\n", recv_len, recv_buff);

                    } else {
                /*one fd  can be read.*/
                for (i = 0; i < MAX_FD_NUM; i++) {
                    if (client_fdset[i] != -1 && FD_ISSET(client_fdset[i], &readfd)) {
                        client_fd = client_fdset[i];
                        break;
                    }
                }

                memset(recv_buff, 0, MAXLINE);
                    recv_len = recv(client_fd, recv_buff, MAXLINE - 1, 0);          
                printf("tcp recv:");
                printf("recv len = %d\n", recv_len);
                if (recv_len <= 0) {
                    if (recv_len == 0) {
                        printf("socket disconnnect(or the other side shutdown socket fd, or network problem)\n");
                        printf("recv socket error: %s(errno: %d)\n",strerror(errno),errno);
                    }
                    else {
                        printf("recv socket error: %s(errno: %d)\n",strerror(errno),errno);
                    }
                    ret = remove_client(client_fd);
                    if (ret != 0) {
                        printf("remove_client() failed ret = %d.\n", ret);
                    }
                    close(client_fd);
                    continue;
                }
                    recv_buff[recv_len] = '\0';
                    printf("recv msg %d byte from client: %s\n", recv_len, recv_buff);

                    if (send(client_fd, response, strlen(response), 0) < 0) {
                        printf("send socket error: %s(errno: %d)\n", strerror(errno), errno);
                        ret = remove_client(client_fd);
                    if (ret != 0) {
                        printf("remove_client() failed ret = %d.\n", ret);
                    }
                    close(client_fd);
                    continue;
                    }

            }

        }
            else    //ret 为0,超时情况
            {
                    printf("time out\n");
            //close(keybd_fd);//产生异常,查看结果
            }

    }

    close(tcp_fd);
    close(udp_fd);
    printf("--------server exit------.\n");
    exit(0);
}




static int init_new_client(int client_fd)
{
    int i;

    for(i = 0; i < MAX_FD_NUM; i++) {
        if (client_fdset[i] == -1) {
            client_fdset[i] = client_fd;    
            break;  
        }
    }

    if (i == MAX_FD_NUM) {
        printf("too many client.\n");
        return -1;
    }

    return 0;
}

static int remove_client(int client_fd)
{

    int i;

    for(i = 0; i < MAX_FD_NUM; i++) {
        if (client_fdset[i] == client_fd) {
            client_fdset[i] = -1;   
            break;  
        }
    }

    if (i == MAX_FD_NUM) {
        printf("client fd is not in list.\n");
        return -1;
    }

    return 0;

}

static int get_max_fd(int fd)
{
    int i;

    for(i = 0; i < MAX_FD_NUM; i++) {
        if (client_fdset[i] > fd) {
            fd = client_fdset[i];
        }
    }

    return fd;
}

client

tcp client 代码如下:

/*
TCP
client
API
usage:
./client 192.168.1.78

notes:
client 端connect()成功之后,此时立刻关闭sockfd断开连接,
server 端recv()会立刻返回,其返回值为0*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>

#define SERVER_PORT 8888
#define MAXLINE 4096

int main(int argc, char** argv)
{
    int    sockfd, n;
    char    recvline[MAXLINE], sendline[MAXLINE];
    struct sockaddr_in    servaddr;
    int recv_len;
    int ret;

    memset(recvline, 0, MAXLINE);
    memset(sendline, 0, MAXLINE);

    if (argc != 2) {
        printf("usage: ./client <ipaddress>\n");
        exit(0);
    }

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
        exit(0);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERVER_PORT);

    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
        printf("inet_pton error for %s/n",argv[1]);
        close(sockfd);
        exit(0);
    }

    if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        printf("connect error: %s(errno: %d)/n",strerror(errno),errno);
        close(sockfd);
        exit(0);
    }


    /*sleep(10);close(sockfd);exit(0);*/
        printf("send msg to server: \n");

        fgets(sendline, MAXLINE - 1, stdin);
    ret = send(sockfd, sendline, strlen(sendline), 0);
    printf("the len of send data = %d.\n", ret);
    if (ret < 0)
        {
            printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
        close(sockfd);
            exit(0);
        } else if (ret == 0) {//return 0
        printf("socket disconnnect(or the other side shutdown socket fd, or network problem) and send error: %s(errno: %d)\n", 
            strerror(errno), errno);
        close(sockfd);
            exit(0);
    }

        recv_len = recv(sockfd, recvline, MAXLINE - 1, 0);
        if (recv_len < 0) {
        printf("recv socket error: %s(errno: %d)\n",strerror(errno),errno);
        close(sockfd);
        exit(0);
        } else if (recv_len == 0) {//return 0
        printf("socket disconnnect(or the other side shutdown socket fd, or network problem) and recv error: %s(errno: %d)\n", 
            strerror(errno), errno);
        close(sockfd);
            exit(0);
    }

        recvline[recv_len] = '\0';
        printf("from server: %s\n", recvline);

        close(sockfd);
    exit(0);
}

udp client 代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<errno.h>
#include<sys/types.h>
int port=8888;

int main(int argc,char *argv[]){
  int sockfd;
  int i=0;
  int z;
  char buf[80],str1[80];
  struct hostent *host;
  struct sockaddr_in adr_srvr;
  if(argc<2){
    fprintf(stderr,"please enter the server's hostname!\n");
    exit(1);
  }

 if((host=gethostbyname(argv[1]))==NULL){
    herror("gethostbyname error!");
    exit(1);
  }

  adr_srvr.sin_family=AF_INET;
  adr_srvr.sin_port=htons(port);
  adr_srvr.sin_addr=*((struct in_addr *)host->h_addr);
  bzero(&(adr_srvr.sin_zero),8);

  sockfd=socket(AF_INET,SOCK_DGRAM,0);
  if(sockfd==-1){
    perror("socket error!");
    exit(1);
  }

  printf("msg to server:\n");
  fgets(buf, sizeof(buf) - 1, stdin);

  printf("send ....\n");
  z=sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&adr_srvr,sizeof(adr_srvr));
  if(z<0){
    perror("sendto error");
    exit(1);
  }


  sprintf(buf,"stop\n");
  z=sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&adr_srvr,sizeof(adr_srvr));
  if(z<0){
    perror("sendto error");
    exit(1);
  }

  close(sockfd);
  exit(0);
}

测试:

编译

$ gcc server_tcp_udp.c -o server_tcp_udp.c
$ gcc tcp_client.c -o tcp_client.c
$ gcc udp_client.c -o udp_client.c

启动server

$ ./server_tcp_udp
$ ./server_tcp_udp 
----------waiting for client's request---------
accept a new  connection from client IP: 127.0.0.1, port: 60858

tcp recv:recv len = 30
recv msg 30 byte from client: Hello Server, I'm tcp client.


udp recv:recv msg 0 byte from client: Hello server, I'm udp client.
udp recv:recv msg 0 byte from client: stop

启动tcp udp client

$ ./client_tcp 127.0.0.1
send msg to server: 
Hello Server, I'm tcp client.
the len of send data = 30.
from server: recv well done.
$ ./client_udp 127.0.0.1
msg to server:
Hello server, I'm udp client.
send ....

可以看到,server端接到tcp client和udp client发送的请求。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值