SOCKS5是个代理服务器协议。
我这里有个可用的基本实现,下面列出的代码不包含框架和辅助功能,仅包含客户端连接建立后的处理,没有认证(因为用的是IP地址限制,连接建立之前就审查过了)。
实现不完整,不支持BIND,FTP协议可以使用PASV绕开这个问题。
没有给出实现的函数基本看名字就可以猜出功能。
头文件:
//servicethreadsocks.h
//SOCKS协议的处理
#ifndef servicethreadsocks_h
#define servicethreadsocks_h
//SOCKS协议处理线程
DWORD ServiceThreadSocks(LPDWORD lpdwParam);
//版本标识/方法选择
STATE SocksMethodSelect(SOCKET s,char * buf,int buflen,int * cmd,int * method);
//方法选择
BYTE SelectMethod(BYTE * msg);
//方法:00
//接收用户请求(方法00)
STATE SocksRecvRequest00(SOCKET s,char * buf,int buflen,int * cmd);
//命令01(CONNECT)处理(方法00)
STATE SocksCmd01Method00(LPDWORD lpdwParam,SOCKADDR_IN *);
//命令03(UDP ASSOCIATE)处理(方法00)
STATE SocksCmd03Method00(LPDWORD lpdwParam,SOCKADDR_IN *);
#endif//servicethreadsocks_h
实现文件:
//servicethreadsocks.cpp
#include "stdafx.h"
#include "mystd.h"
//SOCKS协议处理线程
DWORD ServiceThreadSocks(LPDWORD lpdwParam)
{
//--线程参数处理------------------------
int cdindex;//连接数据索引
struct ServiceData * servicedata;
cdindex=((struct ThreadUserData *)lpdwParam)->index;
servicedata=((struct ThreadUserData *)lpdwParam)->servicedata;
//--------------------------------------
struct ConnectionData * cd;
struct LogStruct * logs;
cd=&servicedata->connectiondataarray.pconnectiondata[cdindex];
if(-1!=cd->log)
{
logs=servicedata->memlogfile.logstruct+cd->log;
}
else
{
logs=NULL;
}
//----------------------------------------
struct ConfigInfo * pci;
pci=&servicedata->serviceconfigfiledata.configarray[cd->serviceindex];
int method;
char host[256];
SOCKADDR_IN sa;
char resp[10];
//构造错误应答
resp[0]=5;//版本
//resp[1]=//应答
resp[2]=0;//保留
resp[3]=1;//IPV4
memset(resp+4,0,6);
//退出?
if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
{
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-1;
}
//版本标识/方法选择过程,method返回选中的方法
//隐含调用SelectMethod选择一个方法
if(1!=SocksMethodSelect(cd->sdc.s,cd->sdc.buf,BUFFERSIZE,&cd->cmd,&method))
{
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-2;
}
//方法子协商结果,仅支持无认证(方法0x00)
switch(method)
{
case 0x00://无认证
break;
case 0xff://没有可接受的方法
default:
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-3;
}
//接收用户请求
if(1!=SocksRecvRequest00(cd->sdc.s,cd->sdc.buf,BUFFERSIZE,&cd->cmd))
{
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-4;
}
//记录日志
if(-1!=cd->log)
{
time(&logs->timestart);
}
//取得目标主机
switch((BYTE)cd->sdc.buf[3])
{
case 0x01://IP V4
memcpy(&(sa.sin_addr.S_un.S_addr),cd->sdc.buf+4,4);
sa.sin_family=AF_INET;
memcpy(&sa.sin_port,cd->sdc.buf+8,2);
if(-1!=cd->log)
{
strcpy(logs->domainname,inet_ntoa(sa.sin_addr));
}
break;
case 0x03://domain name
if(cd->sdc.buf[4]>=256)
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-5;
}
memcpy(host,cd->sdc.buf+5,cd->sdc.buf[4]);
host[cd->sdc.buf[4]]='\0';
if(1!=GetAddrByHost(sa.sin_addr.S_un.S_addr,host))
{
resp[1]='\x04';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-5;
}
sa.sin_family=AF_INET;
memcpy(&sa.sin_port,cd->sdc.buf+4+1+cd->sdc.buf[4],2);
if(-1!=cd->log)
{
strcpy(logs->domainname,host);
}
break;
case 0x04://IP V6
default:
resp[1]='\x08';
send(cd->sdc.s,resp,10,0);//0x08,地址类型不支持
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-6;
break;
}
//转移至命令处理,在命令处理内部结束所有操作
switch(method)
{
case 0x00://无认证
switch(cd->sdc.buf[1])
{
case 0x01://CONNECT
if(pci->isenablesocksconnect)
SocksCmd01Method00(lpdwParam,&sa);
else
{
resp[1]='\x02';//请求因不符合规则而被拒绝
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
}
break;
case 0x03://UDP ASSOCIATE
if(-1!=cd->log)
{
mystrcat(logs->domainname,256," - UDP ASSOCIATE",FALSE);
}
SocksCmd03Method00(lpdwParam,&sa);
break;
case 0x02://BIND
//直接下落
default:
resp[1]='\x07';//命令未实现
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
break;
}
break;
default:
break;
}
//结束,不做任何处理
return 1;
}
//版本标识/方法选择
STATE SocksMethodSelect(SOCKET s,char * buf,int buflen,int * cmd,int * method)
{
BYTE nmethods;
int recvcount=0;
int recvall=0;
BOOL tempbool;
struct timeval timeout;
timeout.tv_sec=0;
timeout.tv_usec=100000;
//接收消息
for(;1;)
{
//退出?
if(CONNECTIONDATA_CMD_QUIT==*cmd)
{
return -1;
}
if(1!=IsSocketReadReady(s,timeout,tempbool))
{
return -2;
}
if(tempbool)
{
recvcount=recv(s,buf+recvall,buflen-recvall,0);
}
else
{
continue;
}
if(SOCKET_ERROR==recvcount)
{
return -3;
}
else if(0==recvcount)
{
return -4;
}
recvall+=recvcount;
if(recvall>=1)
{
if(0x05!=(BYTE)buf[0])
{
return -5;
}
}
if(recvall>2)
{
nmethods=buf[1];
if(recvall>=nmethods+2)
{
break;
}
}
}
//选择方法
*method=SelectMethod((BYTE*)buf);
buf[1]=(BYTE)(*method);
//发送 方法选择 消息
if(SOCKET_ERROR==send(s,buf,2,0))
{
return -6;
}
return 1;
}
//方法选择
BYTE SelectMethod(BYTE * msg)
{
int i;
for(i=msg[1];i;i--)
{
if(0x00==msg[i+1])
{
return msg[i+1];
}
}
return 0xff;
}
//接收用户请求(方法0x00)
STATE SocksRecvRequest00(SOCKET s,char * buf,int buflen,int * cmd)
{
BYTE atyp;
BOOL isrecvend=FALSE;
int recvcount=0;
int recvall=0;
BOOL tempbool;
struct timeval timeout;
timeout.tv_sec=0;
timeout.tv_usec=100000;
//接收消息
for(;1;)
{
//退出?
if(CONNECTIONDATA_CMD_QUIT==*cmd)
{
return -1;
}
if(1!=IsSocketReadReady(s,timeout,tempbool))
{
return -2;
}
if(tempbool)
{
recvcount=recv(s,buf+recvall,buflen-recvall,0);
}
else
{
continue;
}
if(SOCKET_ERROR==recvcount)
{
return -3;
}
else if(0==recvcount)
{
return -4;
}
recvall+=recvcount;
if(recvall>=1)
{
if(0x05!=(BYTE)buf[0])
{
return -5;
}
}
if(recvall>6)
{
atyp=buf[3];
switch(atyp)
{
case 0x01://IP V4
if(recvall>=10)
{
isrecvend=TRUE;
}
break;
case 0x03://domain name
if(recvall>=7+buf[4])
{
isrecvend=TRUE;
}
break;
case 0x04://IP V6
if(recvall>=22)
{
isrecvend=TRUE;
}
break;
}
}
if(isrecvend)
{
break;
}
}
return 1;
}
//*命令01(CONNECT)处理(方法00)
STATE SocksCmd01Method00(LPDWORD lpdwParam,SOCKADDR_IN * psa)
{
//--线程参数处理------------------------
int cdindex;//连接数据索引
struct ServiceData * servicedata;
cdindex=((struct ThreadUserData *)lpdwParam)->index;
servicedata=((struct ThreadUserData *)lpdwParam)->servicedata;
//--------------------------------------
struct ConnectionData * cd;
struct LogStruct * logs;
cd=&servicedata->connectiondataarray.pconnectiondata[cdindex];
if(-1!=cd->log)
{
logs=servicedata->memlogfile.logstruct+cd->log;
}
else
{
logs=NULL;
}
//----------------------------------------
char str[256];
char str2[256];
char * pstr;
char resp[10];
//构造应答
resp[0]=5;//版本
//resp[1]=//应答
resp[2]=0;//保留
resp[3]=1;//IPV4
memcpy(resp+4,&psa->sin_addr.S_un.S_addr,4);
memcpy(resp+8,&psa->sin_port,2);
//建立SOCKET
if(INVALID_SOCKET==(cd->sdr.s=socket(AF_INET,SOCK_STREAM,0)))
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -1;
}
//退出?
if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -2;
}
//连接
if(SOCKET_ERROR==connect(cd->sdr.s,(struct sockaddr *)psa,sizeof(*psa)))
{
resp[1]='\x04';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -3;
}
else
{
resp[1]='\x00';
send(cd->sdc.s,resp,10,0);
if(-1!=cd->log)
{
strcpy(logs->domainname,"SOCKS:CONNECT -> ");
strcpy(str,"");
switch((BYTE)cd->sdc.buf[3])
{
case 0x01://IP V4
if(NULL!=(pstr=inet_ntoa(psa->sin_addr)))
{
strcpy(str,pstr);
}
else
{
strcpy(str,"???");
}
break;
case 0x03://domain name
memcpy(str,cd->sdc.buf+5,cd->sdc.buf[4]);
str[cd->sdc.buf[4]]='\0';
break;
case 0x04://IP V6
strcpy(str,"IP V6 - 未支持的");
break;
}
mystrcat(logs->domainname,256,str,TRUE);
strcpy(str," : ");
itoa(ntohs(psa->sin_port),str2,10);
mystrcat(str,256,str2,TRUE);
mystrcat(logs->domainname,256,str,FALSE);
}
}
//退出?
if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -4;
}
//接收数据并发给客户
TransData(cd->sdr.s,cd->sdc.s,cd->sdr.buf,BUFFERSIZE,&cd->cmd,&cd->sdr.bufcount,servicedata,cd);
//记录字节数
if(-1!=cd->log)
{
logs->bytecount+=cd->sdr.bufcount;
}
//结束
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return 1;
}
//命令03(UDP ASSOCIATE)处理(方法00)
STATE SocksCmd03Method00(LPDWORD lpdwParam,SOCKADDR_IN * psa)
{
//--线程参数处理------------------------
int cdindex;//连接数据索引
struct ServiceData * servicedata;
cdindex=((struct ThreadUserData *)lpdwParam)->index;
servicedata=((struct ThreadUserData *)lpdwParam)->servicedata;
//--------------------------------------
struct ConnectionData * cd;
struct LogStruct * logs;
cd=&servicedata->connectiondataarray.pconnectiondata[cdindex];
if(-1!=cd->log)
{
logs=servicedata->memlogfile.logstruct+cd->log;
}
else
{
logs=NULL;
}
//----------------------------------------
SOCKET udpsocket;//与远程服务器连接,sdr实际与客户连接
int fromlen=sizeof(SOCKADDR_IN);
SOCKADDR_IN udpsa;
char host[256];
char resp[10];
BOOL tempbool;
struct timeval timeout;
timeout.tv_sec=0;
timeout.tv_usec=100000;
int i;
//构造应答
resp[0]=5;//版本
//resp[1]=//应答
resp[2]=0;//保留
resp[3]=1;//IPV4
//建立客户端 UDP SOCKET
if(INVALID_SOCKET==(cd->sdr.s=socket(AF_INET,SOCK_DGRAM,0)))
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -1;
}
//bind
udpsa.sin_family=AF_INET;
udpsa.sin_addr.S_un.S_addr=servicedata->serviceconfigfiledata.configarray[cd->serviceindex].sa.sin_addr.S_un.S_addr;
udpsa.sin_port=0;
if(SOCKET_ERROR==bind(cd->sdr.s,(sockaddr *)&udpsa,sizeof(SOCKADDR_IN)))
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -1;
}
//connect
udpsa.sin_family=AF_INET;
if(0==psa->sin_addr.S_un.S_addr)
udpsa.sin_addr.S_un.S_addr=((SOCKADDR_IN *)(&cd->saSC))->sin_addr.S_un.S_addr;
else
udpsa.sin_addr.S_un.S_addr=psa->sin_addr.S_un.S_addr;
memcpy(&udpsa.sin_port,&psa->sin_port,2);
if(SOCKET_ERROR==connect(cd->sdr.s,(sockaddr *)&udpsa,sizeof(SOCKADDR_IN)))
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -1;
}
i=sizeof(SOCKADDR_IN);
if(SOCKET_ERROR==getsockname(cd->sdr.s,(sockaddr *)&udpsa,&i))
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -1;
}
memcpy(resp+4,&servicedata->serviceconfigfiledata.configarray[cd->serviceindex].sa.sin_addr.S_un.S_addr,4);//设置应答
memcpy(resp+8,&udpsa.sin_port,2);
//建立服务器端 UDP SOCKET
if(INVALID_SOCKET==(udpsocket=socket(AF_INET,SOCK_DGRAM,0)))
{
resp[1]='\x01';
send(cd->sdc.s,resp,10,0);
closesocket(cd->sdc.s);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -1;
}
//发送应答
resp[1]=0;
if(SOCKET_ERROR==send(cd->sdc.s,resp,10,0))
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -2;
}
//退出?
if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
logs->state=LOGSTRUCT_STATE_NOUSE;
}
cd->state=CONNECTION_NOUSE;
return -2;
}
int i_Perm;
IN_ADDR ipSC;
bool bret;
int userindex;
int reason;
//转发数据报
i_Perm=0;
for(;1;)
{
//根据IP检查用户许可
i_Perm++;
if(i_Perm>=300 && servicedata->userfiledata.IsCheckUser)
{
i_Perm=0;
memcpy(&ipSC,cd->saSC.sa_data+2,4);
if(1!=IsUserPermission(&servicedata->userfiledata,ipSC,&bret,&userindex,&reason))break;
if(!bret)break;
}
//检查发起连接是否已经关闭
if(1!=IsSocketReadReady(cd->sdc.s,timeout,tempbool))
{
break;
}
if(tempbool)
{
cd->sdc.bufcount=recv(cd->sdc.s,cd->sdc.buf,BUFFERSIZE,0);
if(0==cd->sdc.bufcount || SOCKET_ERROR==cd->sdc.bufcount)
{
break;
}
}
//检查来自客户的数据
if(1!=IsSocketReadReady(cd->sdr.s,timeout,tempbool))
{
break;
}
if(tempbool)
{
cd->sdr.bufcount=recvfrom(cd->sdr.s,cd->sdr.buf,BUFFERSIZE,0,(sockaddr *)&udpsa,&fromlen);
if(0==cd->sdr.bufcount || SOCKET_ERROR==cd->sdr.bufcount)
{
break;
}
//取得目标主机
switch((BYTE)cd->sdr.buf[3])
{
case 0x01://IP V4
memcpy(&(udpsa.sin_addr.S_un.S_addr),cd->sdr.buf+4,4);
udpsa.sin_family=AF_INET;
memcpy(&udpsa.sin_port,cd->sdr.buf+8,2);
cd->sdr.bufcount-=10;
memmove(cd->sdr.buf,cd->sdr.buf+10,cd->sdr.bufcount);
//MessageBox(NULL,inet_ntoa(udpsa.sin_addr),"IP",MB_OK);//这一句显示目标地址
break;
case 0x03://domain name
memcpy(host,cd->sdr.buf+5,cd->sdr.buf[4]);
host[cd->sdr.buf[4]]='\0';
if(1!=GetAddrByHost(udpsa.sin_addr.S_un.S_addr,host))
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-5;
}
udpsa.sin_family=AF_INET;
memcpy(&udpsa.sin_port,cd->sdr.buf+4+1+cd->sdr.buf[4],2);
cd->sdr.bufcount-=6+1+cd->sdr.buf[4];
memmove(cd->sdr.buf,cd->sdr.buf+6+1+cd->sdr.buf[4],cd->sdr.bufcount);
break;
case 0x04://IP V6
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return (DWORD)-6;
break;
}
//send
if(SOCKET_ERROR==sendto(udpsocket,cd->sdr.buf,cd->sdr.bufcount,0,(sockaddr *)&udpsa,sizeof(SOCKADDR_IN)))
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return -2;
}
if(-1!=cd->log)
{
logs->bytecount+=cd->sdr.bufcount;
}
}
//检查来自远端的数据
if(1!=IsSocketReadReady(udpsocket,timeout,tempbool))
{
break;
}
if(tempbool)
{
cd->sdr.bufcount=recvfrom(udpsocket,cd->sdr.buf,BUFFERSIZE,0,(sockaddr *)&udpsa,&fromlen);
if(0==cd->sdr.bufcount || SOCKET_ERROR==cd->sdr.bufcount || cd->sdr.bufcount>BUFFERSIZE-10)
{
if(SOCKET_ERROR==cd->sdr.bufcount && 10054==WSAGetLastError())
{
continue;
}
break;
}
memmove(cd->sdr.buf+10,cd->sdr.buf,cd->sdr.bufcount);
cd->sdr.bufcount+=10;
cd->sdr.buf[0]=0;
cd->sdr.buf[1]=0;
cd->sdr.buf[2]=0;
cd->sdr.buf[3]=1;
memcpy(cd->sdr.buf+4,&udpsa.sin_addr.S_un.S_addr,4);
memcpy(cd->sdr.buf+8,&udpsa.sin_port,2);
//MessageBox(NULL,inet_ntoa(udpsa.sin_addr),"Source IP",MB_OK);//这一句显示源地址
//send
if(SOCKET_ERROR==send(cd->sdr.s,cd->sdr.buf,cd->sdr.bufcount,0))
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return -2;
}
if(-1!=cd->log)
{
logs->bytecount+=cd->sdr.bufcount;
}
}
//退出?
if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
{
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return -2;
}
}
closesocket(cd->sdc.s);
closesocket(cd->sdr.s);
closesocket(udpsocket);
if(-1!=cd->log)
{
time(&logs->timeend);
logs->state=LOGSTRUCT_STATE_USED;
}
cd->state=CONNECTION_NOUSE;
return 1;
}
(这里是结束)