![]() |
![]() |



Author: RichardGreen
Date Submitted: 2003-12-21 11:15:00
In article.
Design
As part of the game i'm "working on" i needed to be able to store and retrieve race times from the server and display a "News" page or a scrolling ticker in the UI. I decided that the best way to do this was via php and HTTP GET.
After looking at the cipher networking code and finding it was all UDP and not TCP (as was to be expected looking back at it now) i realised i would have to modify a number of functions, these were:
<ul>
<li>[Code]winsock_OpenSocket[/Code]
<li>[Code]winsock_Write[/Code]
<li>[Code]winsock_Read[/Code]
</ul>
My first attempt included passing a bool as an extra param to these functions to speficy if they where TCP or UDP, how ever after looking at winsock_OpenSocket i decided i would rewrite the functions as needed, following the cipher naming convention the functions are named:
<ul>
<li>[Code]winsock_TcpOpenSocket[/Code]
<li>[Code]winsock_TcpWrite[/Code]
<li>[Code]winsock_TcpRead[/Code]
</ul>
As anyone who knows network programming knows UDP is state-less and TCP is stated, this meant i had to create a totally new function to connect the socket our end to the server, this function is named winsock_TcpConnect.
The source code for these 4 functions is presented below and is placed in net_winsock.h:
/*
==========================
winsock_OpenTcpSocket
Opens a socket
==========================
*/
int winsock_OpenTcpSocket(int port)
{
int newsocket;
struct sockaddr_in address;
// create a new socket
newsocket = socket(PF_INET, SOCK_STREAM, 0);
if (newsocket==INVALID_SOCKET)
{
con_Print(_T("winsock: Failed to open socket\n"));
return -1;
}
// prepare the sockets name
com_memset(&address, 0, sizeof(address));
address.sin_family = PF_INET;
address.sin_addr.s_addr = wsd.local_IPaddress;
address.sin_port = htons((u_short)port);
// bind to it
if (bind(newsocket, (struct sockaddr*)&address, sizeof(address))==SOCKET_ERROR)
{
// failed to bind to the socket
con_Print(_T("winsock: Failed to bind to address\n"));
closesocket(newsocket);
return -1;
}
// worked
return newsocket;
}
/*
==========================
winsock_TcpConnect
connects to a remote machine via TCP
==========================
*/
int winsock_TcpConnect(int socket, netaddress_t* address)
{
struct sockaddr_in sockaddress;
// convert the address and send the data
winsock_NetAddressToSockAddress(address, &sockaddress);
//SOCKET_ERROR
return connect(socket,(SOCKADDR*)&sockaddress,sizeof(sockaddress));
}
/*
==========================
winsock_TcpWrite
Sends 'len' bytes of data to the machine at 'address'
==========================
*/
int winsock_TcpWrite(int socket, byte* buf, int len, netaddress_t* address)
{
return send(socket, buf, len, 0);
}
/*
==========================
winsock_TcpRead
Reads bytes off the network.
returns the number of bytes we got. <0 is an error
If it successfully reads data from the network, address will
hold the address of the machine that sent the data
==========================
*/
int winsock_TcpRead(int socket, byte* buf, int len, netaddress_t* address)
{
int bytesread, totalbytesread;
totalbytesread = 0;
do{
bytesread = recv(socket, buf, len, 0);
totalbytesread += bytesread;
}while((bytesread != SOCKET_ERROR) && bytesread!=0 && bytesread != WSAECONNRESET);
// convert the returned address
return bytesread;
}
[/Code]
The next bit was to actually write the URL retreval, I decided to stick to HTTP 1.0 for the first version as its very slightly easier to implement than 1.1 (altough the difference is tiny and it would take about 2 minutes to make it 1.1).
After looking at the cipher calls that can be made from cgame,game and UI the function was prototyped like:
[code]void net_LoadURL(const tchar_t *URL, tchar_t* data, int len);
/*
==========================
net_LoadURL
Reads the data from a URL using HTTP/1.0
==========================
*/
void net_LoadURL(const tchar_t *URL, tchar_t* data, int len)
{
int socket;
int conn_state;
int amount_sent;
int URLLen;
int i,t;
int ServerLen;
int Done;
netaddress_t addr;
byte *buffer;
tchar_t Request[MAX_STRING_SIZE];
tchar_t Server[MAX_STRING_SIZE];
tchar_t *URLCopy;
buffer = (byte*)com_Malloc(len);
com_zeromem(buffer, len);
com_zeromem(Server, MAX_STRING_SIZE);
URLLen = com_strlen(URL);
URLCopy = com_strdup(URL);
ServerLen = 0;
Done = 0;
i=0;
while(Done == 0 && i < URLLen)
{
if(i < 7)
{
*URLCopy++;
}else{
char *temp = sys_AsMultiByte(URLCopy);
if(temp[i-7] == '/')
{
ServerLen++;
Done = 1;
}else{
ServerLen++;
}
}
i++;
}
com_strncpy(Server,URLCopy,ServerLen);
// Reset URLCopy pointer to start so we don't get heap corruption
for(t=0 ;t<7;t++)
*URLCopy--;
if(com_strchr(Server,':') == NULL)
{
// We don't have a port so assume port 80
com_Sprintf(Server,MAX_STRING_SIZE,_T("%ls:80"),Server);
}
socket = winsock_OpenTcpSocket(0);
if(socket == -1)
{
con_Printf(_T("Couldn't open socket to load url from\n"));
return;
}
winsock_NameToAddress(Server, &addr);
com_Sprintf(Request,MAX_STRING_SIZE,_T("GET %ls HTTP/1.0\r\n\r\n\r\n"),URL);
conn_state = winsock_TcpConnect(socket,&addr);
// con_Printf(_T("State: %d\n"),conn_state);
amount_sent = winsock_TcpWrite(socket, sys_AsMultiByte(Request), com_strlen(Request), &addr);
// con_Printf(_T("Sent: %d\n"),amount_sent);
winsock_TcpRead(socket,buffer,len-1,&addr);
winsock_CloseSocket(socket);
// All done
sys_MultiByteToWide((const char*)buffer,data,len-1);
com_Free(buffer);
com_Free(URLCopy);
}
// RG - LoadURL Support
void cipher_LoadURL(const tchar_t* URL, tchar_t *dst, int len)
{
cipher(UI_LOADURL,URL,dst,len);
}
tchar_t dst[1024];
cipher_LoadURL(_T("http://www.codeproject.com/"),dst,1024);
cipher_con_Print(dst);
[Recent Contributions] [Recent Source Code]
User Contributed Comments
int winsock_TcpRead(int socket, byte* buf, int len, netaddress_t* address, winsock_Progress proc)
{
int bytesread, totalbytesread;
totalbytesread = 0;
do{
bytesread = recv(socket, buf, len, 0);
if (bytesread > 0)
{
totalbytesread += bytesread;
buf += bytesread;
len -= bytesread;
}
}while((bytesread != SOCKET_ERROR) && bytesread!=0 && bytesread != WSAECONNRESET && len > 0);
// convert the returned address
return totalbytesread;
}