📄 ztcp.c
字号:
//Copyright (C) 1997-2001 ZSNES Team ( zsknight@zsnes.com / _demo_@zsnes.com )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later
//version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/**********************************************************\
* ZSNES TCP/IP MODULE FOR NETWORK PLAY *
* *
* Coded by the ZSNES team *
* TCP/IP drivers coded by _Demo_, revised by Pharos *
* UDP drivers coded by _Demo_, revised by zsKnight *
* Gameplay netplay implementation by zsKnight *
* UDP Packet loss/out of order algorithm/implementation *
* by zsKnight, assistance on normal packets by Pharos *
\**********************************************************/
// UDP Algorithm:
//
// UDP Header (1 byte): 1 = Normal Packet w/ reply req, 2 = Reply Packet,
// 3 = Gameplay Packet (single byte),
// 4 = Gameplay Packet (larger packet), 5 = Re-request gameplay
// packet
//
// Normal Packets:
// Note: The actual implementation turned out to be quite different
// than the below descriptions.
// First byte contains the packet counter, followed by packet contents.
// Remote will send a Reply Packet (just contains packet counter)
// Each packet buffer will have a timer counter which decreases after
// every 1/60 seconds (value set at start is 60). If this reaches 0
// that packet will be re-sent and reset the timer value back to 60.
// If the local side receives the reply packet, it will set the timer
// counter to -1.
//
// Gameplay Packets:
// Note: Gameplay counter is separate from normal packet counter.
// Note2: When referring to TCP/IP, it refers to the Normal Packets above.
// Each packet in TCP/IP will contain a byte counter when UDP is
// enabled.
// Each UDP packet will contain a byte counter, the number of packets,
// then each packet will contain a byte size only if there are > 1
// packets. If the packet is just one byte long and contains a value<2,
// it will follow by a byte containing info on how many packets its has
// been like that for (it will not go beyond 32). If the packet is
// more than one byte long, it will repeat that packet as the extra
// packets for the next 3 packets, with the first byte of those packets
// as the byte counter of that packet, then the second as the size.
// Also, the send data will be stored in a 256*32 byte buffer in case
// of packet loss.
// When receiving, since no UDP packets will exceed 32bytes in length,
// there will be a 256*32 byte buffer and a 256 byte flag buffer.
// The flag clearing pointer will move at an offset of 128 from the
// actual point of the receive buffer. When it receives data from
// the UDP (or TCP), if the byte count of the data matches the
// receive pointer, it will just send the data directly and increase the
// receive pointer. Else it will fill the buffer accordingly based on
// the send data (for a maximum of 32 bytes). Then if the bit on the
// flag buffer is set for the current receive pointer, return the
// appropriate buffer and increase receive pointer.
// In case of packet loss, if no data has been received for every 500ms, the
// local side would send a re-send package request. What this would
// do is let the remote side build up a package containing all the
// data from the requested send point to the current receive point.
// A resend request will start off with 0x00,0xFF, then the counter
// number. A resent packet will start off with 0x00,0xFE, the # of
// packets, then the packet data (size of packet, data). A resend will
// only be done if the requested packet is within the past 64 packets.
// In-game chat will be moved to a separate packet in TCP/IP
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef __LINUX__
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/ioctl.h>
#ifndef __FreeBSD__
#include <asm/ioctls.h> // for FIONREAD
#else
#include <sys/filio.h> // for FIONREAD
#endif
#else
#include <windows.h>
#include <winsock.h>
#endif
#ifdef __LINUX__
#define closesocket(A) close(A)
#define CopyMemory(A,B,C) memcpy(A,B,C)
#define STUB_FUNCTION fprintf(stderr,"STUB: %s at " __FILE__ ", line %d, thread %d\n",__FUNCTION__,__LINE__,getpid())
#define UINT unsigned int
#define WORD unsigned short
#define SOCKET int
#define SOCKADDR_IN struct sockaddr_in
#define LPSOCKADDR struct sockaddr*
#define LPHOSTENT struct hostent*
#define HOSTENT struct hostent
#define LPINADDR struct in_addr*
#define LPIN_ADDR struct in_addr*
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif
int RecvPtr;
int RecvPtr2;
unsigned char RecvFlags[256];
unsigned char RecvBuffer[256*32];
int RecvBufferSize[256];
int SendPtr;
int SendPtr2;
unsigned char SendBuffer[256*32];
int SendBufferSize[256];
int SendRepeated;
int PrevSPacket[16];
int PrevSData[16*32];
int PrevSSize[16];
int PrevSPtr[16];
int tcperr;
unsigned short portval;
int UDPEnable = 1;
int UDPConfig = 1;
int UDPBackTrace = 6;
int blahblahblah = 0;
int CounterA = -1;
int CounterB = -1;
int UDPMode2 = 0;
int packetnum,packetnumhead;
int packetrecvhead;
unsigned char packetdata[2048*16];
unsigned char packetrdata[2048*32];
int packetconfirm[256];
int packetreceived[256];
int packetreceivesize[256];
int packetsize[256];
unsigned char cpacketdata[2048+32];
UINT ConnectAddr;
int packettimeleft[256];
int packetresent[256];
int PacketCounter=0;
unsigned char CLatencyVal=0;
SOCKET gamesocket; /* tcp socket for the game */
SOCKET serversocket; /* tcp socket when the server is listening */
SOCKET ugamesocket; /* udp socket sending */
SOCKET userversocket; /* udp socket listening */
SOCKADDR_IN serveraddress; /* address of the server */
SOCKADDR_IN ugameaddress; /* address of the server */
SOCKADDR_IN userveraddress; /* address of the server */
char blah[256];
char remotehost[256];
char hostname[50] = "IP N/A";
// Function Prototypes
int SendData(int dsize,unsigned char *dptr);
int GetData(int dsize,unsigned char *dptr);
/**********************************************************\
* Initialize the zsnes tcpip module *
* - no parameters *
* - return 0 on success other value on error *
* *
* - no known side effects *
\**********************************************************/
int InitTCP()
{
#ifndef __LINUX__
char blah[255];
WORD versionneeded = MAKEWORD(2,2);
WSADATA wsadata;
#endif
UDPEnable=0;
#ifndef __LINUX__
/* Startup winsock */
WSAStartup(versionneeded, &wsadata);
/* Verify version number and exit on wrong version */
if (wsadata.wVersion != versionneeded)
{
return(-1);
}
serversocket=INVALID_SOCKET;
#endif
return(0);
}
/**********************************************************\
* Deinitialize the zsnes tcpip module *
* - no parameters *
* *
* - no known side effects *
\**********************************************************/
void DeInitTCP()
{
#ifndef __LINUX__
WSACleanup();
#endif
}
/**********************************************************\
* Gets UDP Status through sending data *
* - no parameters *
* *
* - no known side effects *
\**********************************************************/
void GetUDPStatus() {
int retval;
UDPEnable=UDPConfig;
if (!UDPEnable){
blah[0]=0;
retval = send(gamesocket,blah,1,0);
gethostname(blah,255);
retval = send(gamesocket,blah,strlen(blah),0);
}
else {
blah[0]=1;
retval = send(gamesocket,blah,1,0);
gethostname(blah,255);
retval = send(gamesocket,blah,strlen(&blah[1])+1,0);
}
retval = recv(gamesocket,blah,256,0);
if (blah[0]==0) UDPEnable=0;
retval = recv(gamesocket,blah,256,0);
}
/**********************************************************\
* Connect to game server *
* - parameters *
* - pointer server name *
* - server port *
* - return 0 on success other value on error *
* *
* - no known side effects *
\**********************************************************/
int isipval(char *name){
int i;
i=0;
while(name[i]!=0){
if (!((name[i]=='.') || ((name[i]>='0') && (name[i]<='9'))))
return(0);
i++;
}
return(1);
}
int ConnectServer(char *servername, unsigned int port)
{
char blah[255];
int retval,i;
LPHOSTENT host1=NULL;
int yesip;
#ifndef __LINUX__
WSADATA wsadata;
#endif
packetnum = 0;
packetnumhead = 0;
packetrecvhead = 0;
RecvPtr = 0;
SendPtr = 0;
RecvPtr2 = 0;
SendPtr2 = 0;
ConnectAddr = 0;
SendRepeated = 0;
for (i=0;i<16;i++)
PrevSPacket[i]=0;
/* get host and verify if it is valid */
yesip = isipval(servername);
if (!yesip){
host1 = gethostbyname(servername);
if (host1 == NULL)
{
return(-1);
}
}
// return(-1);
if (UDPConfig) UDPEnable = 1;
if (UDPEnable)
{
PacketCounter=1;
for (i=0;i<256;i++) {packettimeleft[i]=-1; packetconfirm[i]=1; packetreceived[i]=0; RecvFlags[i]=0;}
userveraddress.sin_family = AF_INET;
ugameaddress.sin_family = AF_INET;
if (!yesip)
{
ugameaddress.sin_addr = *( (LPIN_ADDR) *host1->h_addr_list );
}
else
{
ugameaddress.sin_addr.s_addr = inet_addr(servername);
}
ConnectAddr = ugameaddress.sin_addr.s_addr;
userveraddress.sin_addr.s_addr = INADDR_ANY;
// port++;
ugameaddress.sin_port = htons((unsigned short) port);
userveraddress.sin_port = htons((unsigned short) port);
// port--;
userversocket = socket(AF_INET, SOCK_DGRAM,0);
ugamesocket = socket(AF_INET, SOCK_DGRAM,0);
if (ugamesocket == INVALID_SOCKET)
{
#ifdef __LINUX__
STUB_FUNCTION;
#else
tcperr=WSAGetLastError();
sprintf(blah,"Could not initialize UDP(2) : %d",tcperr);
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
#endif
return(-2);
}
if (userversocket == INVALID_SOCKET)
{
#ifdef __LINUX__
STUB_FUNCTION;
#else
tcperr=WSAGetLastError();
sprintf(blah,"Could not initialize UDP(2.5) : %d",tcperr);
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
#endif
return(-2);
}
if (bind(userversocket,(struct sockaddr*)&userveraddress,sizeof(userveraddress))==
SOCKET_ERROR)
{
#ifdef __LINUX__
STUB_FUNCTION;
#else
tcperr=WSAGetLastError();
sprintf(blah,"Could not initialize UDP(16) : %d",tcperr);
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
#endif
return(-2);
}
// blah[0]=1;
// retval = sendto(ugamesocket,blah,1,0,(struct sockaddr*)&ugameaddress,sizeof(struct sockaddr));
// if (retval == SOCKET_ERROR) return(-1);
blah[0]=1;
SendData(1,blah);
// retval = sendto(ugamesocket,blah,5,0,(struct sockaddr*)&ugameaddress,sizeof(struct sockaddr));
// blah[0]=0;
// i = sizeof(struct sockaddr);
// retval = recvfrom(userversocket,blah,5,0,(struct sockaddr*)&userveraddress,&i);
// MessageBox(NULL,blah,
// "Error",
// MB_SYSTEMMODAL|MB_OK);
return(0);
// retval = send(gamesocket,blah,1,0);
// retval = recv(gamesocket,blah,1,0);
}
/* create the game socket and verify if it is valid */
gamesocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (gamesocket == INVALID_SOCKET)
{
return(-2);
}
/* initialize server address */
serveraddress.sin_family = AF_INET;
if (!yesip)
serveraddress.sin_addr = *( (LPIN_ADDR) *host1->h_addr_list );
else
serveraddress.sin_addr.s_addr = inet_addr(servername);
serveraddress.sin_port = htons((unsigned short)port);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -