1. 程式人生 > >同一埠監聽tcp和udp請求

同一埠監聽tcp和udp請求

問題:

眾所周知,同一臺機器的同一個埠只可以被一個程序使用,一般用於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傳送的請求。