3.SDL2_net TCP服務器端和多個客戶端 續

上一節缺少了tcputil的講解,首先這幾個函數都不是我寫的,一部分是從SDL_net官網上下載的小例子,另一部分是找的github上的代碼。

tcputil.h

#ifndef tcputil_h
#define tcputil_h 1

#ifdef WIN32
#else
        #include <sys/socket.h>
        #include <sys/ioctl.h>
        #include <net/if.h>
        #include <arpa/inet.h>
#endif

#include <string>

#include "SDL.h"
#include "SDL_net.h"

/* receive a buffer from a TCP socket with error checking */
/* this function handles the memory, so it can't use any [] arrays */
/* returns 0 on any errors, or a valid char* on success */
extern char *getMsg(TCPsocket sock, char **buf);

/* send a string buffer over a TCP socket with error checking */
/* returns 0 on any errors, length sent on success */
extern int putMsg(TCPsocket sock, const char *buf);
/**
 * 獲取當前的IP地址
 * @param num_ip ip的數字
 * @return 格式化的ip地址
 */
extern std::string getLocalHostIP(Uint32& num_ip);

#endif

putMsg負責發送信息,getMsg負責接收信息,新加的getLocalHostIP()則負責獲取當前主機的IP地址,這個函數在局域網遊戲中尤其有用,比如局域網對戰,該函數僅僅在ubuntu下測試成功,暫時未在window下測試。

#include "tcputil.h"

char *getMsg(TCPsocket sock, char **buf)
{
        Uint32 len,result;
        static char *_buf;

        /* allow for a NULL buf, use a static internal one... */
        if(!buf)
                buf=&_buf;
    
        /* free the old buffer */
        if(*buf)
                free(*buf);
        *buf=NULL;

        /* receive the length of the string message */
        result=SDLNet_TCP_Recv(sock,&len,sizeof(len));
        if(result<sizeof(len))
        {
                if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
                        printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
                return(NULL);
        }
    
        /* swap byte order to our local order */
        len=SDL_SwapBE32(len);
    
        /* check if anything is strange, like a zero length buffer */
        if(!len)
                return(NULL);

        /* allocate the buffer memory */
        *buf=(char*)malloc(len);
        if(!(*buf))
                return(NULL);

        /* get the string buffer over the socket */
        result=SDLNet_TCP_Recv(sock,*buf,len);
        if(result<len)
        {
                if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
                        printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
                free(*buf);
                buf=NULL;
        }

        /* return the new buffer */
        return(*buf);
}

getMsg負責獲取數據,這一對函數必須配套使用,因爲putMsg函數在發送真正的信息之前,會先發送本次要發送的字節數,而getMsg也會先獲取字節數,並分配好適當的空間。另外,getMsg()內部採取了靜態局部變量來設置內存,因此,不必擔心內存問題。

int putMsg(TCPsocket sock,const char *buf)
{
        Uint32 len,result;

        if(!buf || !strlen(buf))
                return(1);

        /* determine the length of the string */
        len=strlen(buf)+1; /* add one for the terminating NULL */

        /* change endianness to network order */
        len=SDL_SwapBE32(len);

        /* send the length of the string */
        result=SDLNet_TCP_Send(sock,&len,sizeof(len));
        if(result<sizeof(len)) {
                if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
                        printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
                return(0);
        }

        /* revert to our local byte order */
        len=SDL_SwapBE32(len);

        /* send the buffer, with the NULL as well */
        result=SDLNet_TCP_Send(sock,buf,len);
        if(result<len) {
                if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
                        printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
                return(0);
        }

        /* return the length sent */
        return(result);
}

putMsg和getMsg函數相對,putMsg主要是向套接字發送數據。

std::string getLocalHostIP(Uint32& num_ip)
{
#ifdef WIN32
        char text[20] = {};
        IPaddress localhost_ip;
        SDLNet_ResolveHost(&localhost_ip, nullptr, 0);
        SDLNet_ResolveHost(&localhost_ip, SDLNet_ResolveIP(&localhost_ip), 0);
        //output
        num_ip = SDL_SwapBE32(localhost_ip.host);
        sprintf(text, "%d.%d.%d.%d",
                        num_ip>>24,
                        (num_ip>>16) & 0xff,
                        (num_ip>>8) & 0xff,
                        (num_ip & 0xff));
        return std::string(text);
#else
        std::string ip;
        int socket_fd = socket(PF_INET,SOCK_DGRAM,0);
        struct sockaddr_in *sin;
        ifconf conf;
        struct ifreq* ifr;
        char buf[128];
        int i,n;
        conf.ifc_len = 128;
        conf.ifc_buf = buf;
        ioctl(socket_fd,SIOCGIFCONF,&conf);
        ifr = conf.ifc_req;
        n = conf.ifc_len/sizeof(struct ifreq);
        for(i=0;i<n;i++)
        {
                sin = (struct sockaddr_in*)(&ifr->ifr_addr);
                ip = inet_ntoa(sin->sin_addr);
                if(ip.compare("127.0.0.1"))
                {
                        return ip;
                }
                ifr++;
        }
        ip = "127.0.0.1";
        return ip;
#endif
}

最後則是getLocaHostIP()這個函數,此函數是我在sdlgui貼吧裏找到的一部分代碼,該代碼作者目前不再進行維護,不過有的代碼還是可圈可點的(我不懂socket編程,我只是大自然的搬運工)。

本節結束。

SDL_net 官方鏈接:http://www.libsdl.org/projects/SDL_net/docs/index.html

sdl_gui github鏈接:https://github.com/SDLGUI/SDLGUI

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章