zoukankan      html  css  js  c++  java
  • linux下服务端实现公网数据转发

    之前在腾讯上使用了一个免费的公网服务器,只有7天,linux系统。

    其实有这样的想法,是因为有个研二的师弟问我怎么样才能让连个局域网的电脑通信。

    我跟他说了两种方法,一种是找个公网服务器来转发数据,另一种就是UDP打洞。

    第二种太难了,所以就用第一种。突然有点想自己实现一下的冲动,于是就搞了一个免费的。

    目的是:编写一个服务端,接收一个或者多个客户端。如果一个客户端发送数据,则立刻转发给其他的所有连接上的客户端(除了自己)。

    TCP的连接程序自然很简单。定义协议后,服务端就开始监听。但是accep函数会在这一步挂起,如果接受了一个客户端就继续向下走了。

    所以需要用多线程来实现。我的思想是,accept一个客户端就继续等待一个客户端的连接。

    先上代码:

    server.c

    #include <stdio.h>
    
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <signal.h>
    #include "server.h"
    
    int server_sockfd;
    struct sockaddr_in server_address;
    
    int vector_user[MaxUser];
    struct sockaddr_in vector_useraddr[MaxUser];
    
    int times=0;
    
    pthread_t transferid;
    
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum,sighandler_t handler);
    
    void sig_int(int sign)
    {
        while(1){
            printf("SIGINT
    ");
        }
    }
    void sig_pipe(int sign)
    {
        while(1){
            printf("SIGPIPE
    ");
        }
    }
    
    int SockInit(void)
    {
        int state=-1;
        server_sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(server_sockfd<0)
        {
            printf("Socket error
    ");
            return -1;
        }
        bzero(&server_address,sizeof(server_address));
        server_address.sin_family=AF_INET;
        server_address.sin_addr.s_addr=htonl(INADDR_ANY);//inet_addr("115.159.196.190");
        server_address.sin_port=htons(PORT);
    
        state=bind(server_sockfd,(struct sockaddr*)&server_address,sizeof(server_address));
        if(state<0)
        {
            printf("Bind error
    ");
            return -1;
        }
        state=listen(server_sockfd,2);
        if(state<0)
        {
            printf("Listen error
    ");
            return -1;
        }
        printf("Server is waiting and listening on port:%d
    ",PORT);
        return 0;
    }
    
    int RevData(int cs,char* buf,int maxlen)
    {
        int len=0;
        printf("Ready to get data
    ");
        read(cs,&len,1);
        printf("Get %d data
    ",len);
        read(cs,buf,len);
        return len;
    }
    
    void SendData(int cs,char* buf,int len)
    {
        int sendlen=len;
        send(cs,&sendlen,1,0);
        send(cs,buf,len,0);
    }
    
    void* TranferData(void* arg)
    {
        char buf[1024]={''};
        while(1)
        {
            int len=RevData(*(int*)arg,buf,1024);
            int i=0;
            printf("SocketID is %d: ",*(int*)arg);
            for(i=0;i<len;i++)
            {
                printf("%c",buf[i]);
            }
            printf("
    ");
            for(i=0;i<times;i++)
            {
                if((*(int*)arg)!=vector_user[i])
                {
                    SendData(vector_user[i],buf,len);
                }
            }
        }
        return ((void *)0);
    }
    
    void* UserAdd(void* arg)
    {
        struct sockaddr_in client_address;
        socklen_t client_len=sizeof(client_address);
    
        while(1)
        {
            int client_sockfd=accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);
            if(client_sockfd<0)
            {
                printf("Accpet error
    ");
            }
            else
            {
                printf("%s:%d is connecting
    ",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
                vector_user[times]=client_sockfd;
                vector_useraddr[times]=client_address;
    
                int err= pthread_create(&(transferid), NULL, TranferData, (void*)(&vector_user[times]));
                if ( 0 != err )
                {
                    printf("Can't create thread
    ");
                }
    
                ++times;
                if(times>=MaxUser)
                {
                    printf("Up to MaxUser
    ");
                    break;
                }
            }
        }
        return ((void *)0);
    }

    server.h

    #ifndef SERVER_H_INCLUDED
    
    #define SERVER_H_INCLUDED
    
    
    
    #define PORT (8888)
    #define TencentIP ("115.159.196.190")
    #define MaxUser (1024)
    
    
    
    int SockInit(void);
    void* UserAdd(void* arg);
    int RevData(int cs,char* buf,int maxlen);
    void SendData(int cs,char* buf,int len);
    
    
    
    #endif // SERVER_H_INCLUDED

    main.c

    #include <stdio.h>
    
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <signal.h>
    #include "server.h"
    
    
    extern int times;
    extern int vector_user[MaxUser];
    
    
    
    int main()
    
    {
        char buf[1024];
        int state=0;
        state=SockInit();
        if(state<0)
        {
            printf("Socket init error
    ");
            return -1;
        }
    
        pthread_t threadid;
        int err;
        err = pthread_create(&(threadid), NULL, UserAdd, NULL);
        if ( 0 != err )
        {
            printf("Can't create thread
    ");
            return -1;
        }
    
        while(1)
        {
    
        }
    
    
        printf("Hello world!
    ");
    
        return 0;
    
    }

    server.c中

    SockInit函数就是用来初始化套接字协议的,没什么可说的,无非就是配置一些参数,然后接听端口。

    RevData函数,第一个参数传递是套接字的标识符,因为会有多个标识符。其中,先读一个字符,是因为我定义的协议是先发一个长度,然后再发对应长度的数据。(仅是为了方便)

    SendData函数也是一样的道理。

    TransferData函数是一个线程函数,传入的参数是套接字的标识符。作用就是把收到的数据转发给除它自己以外所有连接的客户端。

    UserAdd函数是一个主要的线程函数。它只开启一次。从while(1)开始就等待accept,如果有连接上,就继续向下走,并且开启TransferData函数线程,之后又进入进的accept,等待新的用户来。每当有用户连接上,都会进入这个状态。

    其中有有个全局数组,vector_user,vector_useraddr。是用来储存加入进来的用户标识符和地址。

    这也是这个程序没有做好的地方:因为如果有客户端掉线,没有那部分代码来去除数组信息,程序就会错误。这是因为,我懒得写一个vector或者其他的数据结构来管理,如果是c++我就用stl了。

    还有一个问题:没有管理好如果有用户突然掉线的情况。可以使用signal来管理,但是时间紧迫,没有完全做好。

    总之:对于简单粗暴的数据转发是可以的。但是程序没有优化,很容易错误。只是想体验下linux C的开发。希望以后能有机会能涉及到这方面的项目。学会linux下快速调试。

  • 相关阅读:
    linux添加开机启动项、登陆启动项、定时启动项、关机执行项等的方法
    linux下/etc/rc.d目录的介绍及redhat启动顺序
    Linux开机自动挂载存储的两种方式
    Linux中环境变量文件profile、bashrc、bash_profile之间的区别和联系
    linux命令详解——yum
    linux命令详解——ftp
    Shell脚本之sed详解
    shell awk读取文件中的指定行的指定字段
    MySQL的字符集
    shell脚本中的数组
  • 原文地址:https://www.cnblogs.com/wyc199288/p/6008193.html
Copyright © 2011-2022 走看看