1>Winsock初始化 (有连接)
a>加载Winsock DLL
int WSAStartup ( WORD wVersionRepuested, //指定准备加载的版本,
LpWSADATA lpWSAData
);
说明: 版本由宏MAKEWORD(X,Y)获得,X是高位字节(副版本),Y是低位字节(主版本),
LPWSADATA 结构:
typedef struct WSAData
{
WORD wVersion, //准备使用的Winsock版本
WORD wHighVersion, //存放现有的最高版本
char szDescription[WSADESCRIPTION_LEN + 1], //无用
char szSystemStatus[WSASYS_STATUS_LEN + 1], //无用
unsigned short iMaxSockets, //不要使用
unsigned short iMaxUdpDg, //不要使用
char FAR * lpVendorInfo, //不用
}WSADATA,FAR* LPWSADATA;
b>错语检查和控制
int WSAGetLastError (void);
c>面向连接的协议
i>bind
int bind (SOCKET s,
const struct sockaddr FAR * name,
int namelen
);
例:
SOCKET s;
struct sockaddr_in tcpaddr;
int port=5150;
s = socket( AF_INET,SOCK_STEAM,IPPROTO_TCP);
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);
tcpaddr.sin_addr.s_addr=htonl(INADDR_ANY);
bind( s, (SOCKADDR * ) &tcpaddr, sizeof(tcpaddr) );
ii>listen
int listen ( SOCKET s,
int backlog //指定正在等待连接的最大队列长度
);
iii>accept 和 WSAAccept
SOCKET accept ( SOCKET s, //处在监听模式的套接字
struct sockaddr FAR * addr, //SOCKADDR_IN结构的地址,返回后包含请求方IP
int FAR * addrlen //SOCKADDR_IN结构的长度
); //成功后,会返回一个新套接字,与客户机沟通以后就用此新套接字
SOCKET WSAAccept ( SOCKET s,
struct sockaddr FAR * addr,
LPINT addrlen,
LPCONDITIONPROC lpfnCondition, //函数指针(如下),根据客户请求来调用,它决定接受请求
DWORD dwCallbackData);
说明: int CALLBACK ConditionFunc( LPWSABUF lpCallerID, //值参数,包含连接实体的地址,
LPWSABUY lpCallerData,//随连接请求发过来的任何连接数据
LPQOS lpSQOS,
LPQOS lpGQOS,
LPWSABUF lpCalleeID, //包含本地IP
LPWSABUF lpCalleeData,//对lpCallerData的补充
GROUP FAR * g,
DWORD dwCallbackData
);
typedef struct __WSABUF
{
u_long len; //其值要么由buf指向的缓冲区长度,要么指定包含在缓冲区buf中的数据量
char FAR * buf;
} WSABUF,FAR * LPWSABUF;
说明:对 lpCallerID来说,buf指向一个地址结构如SOCKADDR类型,所以包含了客户机IP
d>connect 和 WSAConnect 函数
int connect (SOCKET s,const struct sockaddr FAR * name,int namelen)
int WSAConnect ( SOCKET s,const struct sockaddr FAR * name,int namelen,
LPWSABUF lpCallerData, //收发请求接接时的数据,包含客户机向服务器发的请求连接的数据
LpWSABUF lpCalleeData, //收发请求连接时的数据,包含服务器向客户机返回的建立连接的数据
LPQOS lpSQOS,
LPQOS lpGQOS);
切记,所有关系到收发数据的缓冲都属于char类型,无Unicode版本,使用Unicode时,把字符串当作char * 或造型为char * 发送
利用字符串长度函数告诉Winsock API函数收发的数据有多少字符时,必须将这个值乘以2,另外一种就是在将字符串数据投给
Winsock API 函数之前,用WideCharToMultiByte把UNICODE转换成ASCII码
e>send 和 WSASend
int send (SOCKET s,
const char FAR * buf,
int len,
int flags); //若成功,返回发送的字节数
int WSASend (SOCKET s,
LPWSABUF lpBuffers, //指向一个或多个WSABUF结构的指针,
DWORD dwBufferCount, //准备投递的结构数
LPDWORD lpNumberOfBytesSent,//指向DWORD(WSASend调用返回的)指针,其中包含总发送数,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped, //用于重叠I/O
LPWSAOVERLappED_COMPLETION_ROUTINE lpCompletROUTINE //用于重叠I/O
);
f>recv 和 WSARecv
int recv( SOCKET s,
char FAR * buf, //即将收到的字符缓冲,
int len, //准备接收的字节数或buf缓冲的长度,
int flags //0,或MSG_PEEK,MSG_OOB 或是它们的接位运算,MSG_PEEK 使数据复制到所提供的接收端缓冲,但系统还有
);
int WSARecv ( SOCKET s,
LPWSABUF lpBuffers, //WSABUF 结构组成的数组
DWORD dwBufferCount, //前一个数组中WSABUF结构的数目
LPDWORD lpNumberOfBytesRecvd, //指向执行这个函数调用所收到的字节数
lpDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
);
g>保证流式套接字所有的数据都发送出去,代码如下:
char sendbuff[2048];
int nBytes = 2048,
nLeft,
idx;
nLeft = nBytes;
idx = 0;
while (nLeft>0)
{
ret=send(s,&sendbuff[idx],nLeft,0);
if (red==SOCKET_ERROR)
{ //Error}
nLeft-=ret;
idx +=ret;
}
h>中断连接
int shutdown (SOCKET s, //
int how //可以是 SD_RECEIVE,SD_SEND或SD_BOTH,即不允许再调用相关函数
);
int closesocket (SOCKET s);
2>例子:
a>回应服务器代码
#include <winsock2.h>
#include <stdio.h>
#define DEFAULT_PORT 5150
#define DERAULT_bUFFER 4096
int port = DEFAULT_PORT;
BOOL bInterface = FALSE,
bRecvOnly =FALSE;
char szAddress[128];
void usage()
{
printf ("usage: server [-p:x] [-i:IP] [-o] /n/n");
printf (" -p:x port number to listen on/n");
printf (" -i:str Interface to listen on/n");
printf (" -o Don't echo the data back/n/n");
ExitProcess(1);
}
void ValidateArgs ( int argc,char ** argv)
{
int i;
for (i=1;i<argc;i++)
{
if ((argv[i][0]=='-')||(argv[i][0]=='/'))
{
switch (tolower(argv[i][1]))
{
case 'p':
iPort = atoi(&argv[i][3]);
break;
case 'i':
bInterface = TRUE;
if (strlen(argv[i]>3)
strcpy (szAddress,&argv[i][3]);
break;
case 'o':
bRecvOnly = TRUE;
break;
default :
usage();
break;
}
}
}
}
// 线程
DWORD WINAPI ClientThread ( LPVOID lpParam )
{
SOCKET sock=( SOCKET ) lpParam;
char szBuff[DEFAULT_BUFFER];
int ret,
nLeft,
idx;
while (1)
{
ret = recv(sock,szBuff,DEFAULT_BUFFER,0);
if (ret==0) //正常返回(没有接收到数据)
break;
else if (ret==SOCKET_ERROR)
{
printf("recv() failed: %d/n",WSAGetLastError());
break;
}
szBuff[ret] = '/0';
printf("RECV: '%s'/n",szBuff);
//如果定义了回复
if (! bRecvOnly)
{
nLeft = ret;
idx = 0;
while (nLeft > 0) //确保发送了所有的数据
{
ret = send (sock,&szBuff[idx],nLeft,0);
if (ret==0)
break;
else if (ret==SOCKET_ERROr)
{
printf ("send() failed: %d/n",WSAGetLastError());
break;
}
nLeft -= ret;
idx += ret;
}
}
}
return 0;
}
//主函数
int main(int argc,char ** argv)
{
WSADATA wsd;
SOCKET sListen,
sClient;
int iAddrSize;
HANDLE hThread;
DWORD dwThreadID;
struct sockaddr_in local,
client;
ValidateArgs (argc,argv);
if (WSAStartup(MAKEWORD(2,2),&wsd)!=0)
{
printf ("Failed to load Winsock!/n");
return 1;
}
slisten = socket (AF_INET,SOCK_STREAM,IPPROTO_IP);
if (sListen==SOCKET_ERROR)
{
printf (" socket() failed:%d/n",WSAGetLastError());
return 1;
}
if (bInterface)
{
local.sin_addr.s_addr = inet_addr(szAddress);
if (local.sin_addr.s_addr==INADDR_NONE)
{ usage();}
}
else
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(iPort);
if (bind(sListen,(struct sockaddr *)&local,sizeof(local))==SOCKET_ERROR)
{
printf ("bind() failed:%d/n",WSAGetLastError());
return 1;
}
listen (sListen,8);
while (1)
{
iAddrSize = sizeof(client);
sClient = accept (sListen,(struct sockaddr *)&client,&iAddrSize);
if (sClient == INVALID_SOCKET)
{
printf ("accept() failed: %d/n",WSAGetLastError());
break;
}
printf("Accepted client: %s:%d/n",inet_ntoa(client.sin_addr),ngohs(client.sin_port));
hThread = CreateThread (NULL,0,ClientThread,(LPVOID)sClient,0,&dwThreadID);
if (hThread ==NULL)
{
printf("CreatThread() failed:%d/n",GetLastError());
break;
}
CloseHandle(hThread);
}
closesocket(sListen);
WSACleanup();
return 0;
}
///
b>客户机代码
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_COUNT 20
#define DEFAULT_PORT 5150
#define DEFAULT_BUFFER 2048
#define DEFAULT_MESSAGE "This is a test of the emergency / broadcasting system"
char szServer[128],
szMessage[1024]; //向服务器发送的信息
int iPort = DEFAULT_PORT;
DWORD dwCount = DEFAULT_COUNt;
BOOL bSendOnly = FALSE; //只发不收标志
void usage()
{
printf ("usage: client [-p:x] [-s:IP] [-n:x] [-o] /n/n");
printf (" -p:x Remote port to send to/n");
printf (" -s:IP Server's IP address or host name/n");
printf (" -n:x Number of timer to send message/n");
printf (" -o Send message only;Don't receive/n");
ExitProcess(1);
}
void ValidateArgs ( int argc,char ** argv)
{
int i;
for (i=1;i<argc;i++)
{
if ((argv[i][0]=='-')||(argv[i][0]=='/'))
{
switch (tolower(argv[i][1]))
{
case 'p':
if (strlen(argv[i])>3)
iPort = atoi(&argv[i][3]);
break;
case 's':
if (strlen(argv[i]>3)
dwCount = atoi(&argv[1][3]);
break;
case 'o':
bSendOnly = TRUE;
break;
default :
usage();
break;
}
}
}
}
int main( int argc,char **argv)
{
WSADATA wsd;
SOCKET sClient;
char szBuffer[DEFAULT_BUFFER];
int ret,
i;
struct sockaddr_in server;
struct hostent *host = NULL;
ValidateArgs( argc,argv);
if (WSAStartup(MAKEWORD(2,2),&wsd)!=0)
{
printf ("Failed to load Winsock librarv!/n");
return i;
}
strcpy( szMessage,DEFAULT_MESSAGE);
sClient = socket (AF_INET,SOCk_STREAM,IPPROTO_TCP);
if (sClient == INVALID_SOCKET)
{
printf ("socket() failed:%d/n",GETLastError());
return 1;
}
server.sin_family = AF_INET;
server.sin_port = htons(iPort);
server.sin_addr.s_addr = inet_addr(szServer);
if(server.sin_addr.s_addr == INADDR_NONE)
{
host = gethostbyname (szServer);
if (host==NULL)
{
printf ("Unable to resolve server: %s/n",szServer);
return 1;
}
CopyMemory (&server.sin_addr,host->h_addr_list[0],host->h_length);
}
if (connect (sClient,(struct sockaddr * )&server,sizeof(server)) == SOCKET_ERROR)
{
//Error
}
for (i=0;i<dwCount;i++)
{
ret = send (sClient,szMessage,strlen(szMessage),0);
if (ret==0) { break;}
else if (ret ==SOCKET_ERROR)
{
//Error
}
printf ("Send %d bytes/n",ret);
if (!bSendOnly)
{
ret =recv (sClient,szBuffer,DefAULT_BUFFER,0);
if (ret ==0) {break;}
else if (ret ==SOCKET_ERROR) {//Error}
}
szBuffer[ret]='/0';
printf ("RECV [%d bytes]:'%s'/n",ret,szBuffer);
}
closesocket (sClient);
WSACleanup();
return 0;
}
3.无连接协议
i>无需listen和accept,只接接收和发送
int recvfrom (SOCKET s,
char FAR* buf,
int len,
int flags,
struct sockaddr FAR * from,
int FAR * fromlen
);
int WSARecvFrom (SOCKET s,
LPWSABUF lpBuffers, //缓冲数组指针
DWORD dwBufferCount, // 指定包含的缓冲数
LPDWORD lpNumberOfBytesRecvd, //返回读取的字节总数
LPDWORD lpFlags,
struct sockaddr FAR * lpFrom, //包含发送方的IP
LPINT lpFromlen, //SOCKADDR结构的长充
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
);
int sendto( SOCKET s,
const char FAR* buf,
int len,
int flags,
const struct sockaddr FAR * to,
int tolen
)
int WSASendTo (SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, //真正发送的字节数
DWORD dwFlags,
const struct sockaddr FAR * lpTo,
int itolen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMpLETION_ROUTINE lpCompletionROUTINE
)
4>无连接的例
i>接收端
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_PORT 5150
#define DEFAULT_COUNT 25
#define DEFAULT_BUFFER_LENGTH 4096
int iPort =DEFAULT_PORT;
DWORD dwCount =DEFAULT_COUNT,
dwLength =DEFAULT_BUFFER_LENGTH;
BOOL bInterface =FALSE;
char szInterface[32];
void usage()
{
printf ("usage:sender [-p:int] [-i:IP] [-n:x] [-b:x]/n/n");
printf (" -p:int Local prot/n");
printf (" -i:IP Local IP address to listen on/n");
printf (" -n:x Number of times to send message/n");
printf (" -b:x Size of buffer jto send/n/n");
ExitProcess (1);
}
void ValidateArgs(int argc,char** argv)
{
int i;
for (i=1;i<argc;i++)
{
if ((argv[i][0]=='-')||(argv[i][0])=='/')
{
switch (tolower(argv[i][1]))
{
case 'p': //Local port
if (strlen(argv[i])>3)
iPort=atoi(&argv[i][3]);
break;
case 'n':
if(strlen(argv[i])>3)
dwLength = atoi(&argv[i][3]);
break;
case 'i':
if (strlen(argv[i])>3)
{
bInterface = TRUE;
strcpy(szInterface,&argv[i][3]);
}
break;
default:
usage();
break;
}
}
}
}
int main (int argc,char ** argv)
{
WSADATA wsd;
SOCKET s;
char *recvbuf = NULL;
int ret,
i;
DWORD dwSenderSize;
SOCKADDR_IN sender,
local;
ValidateArgx(argc,argv);
if (WSAStartup (MAKEWORD(2,2),&wsd)!=0)
{
//Error
}
s = socket(AF_INET,SOCK_DGRAM,0);
if (s==INVALID_SOCKET)
{ //Error }
local.sin_family = AF_INET;
local.sin_port = htons((short)iPort);
if (bInterface)
local.sin_addr.s_addr = inet_addr(szInterface);
else
local.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s,(SOCKADDR *)&local,sizeof(local))==SOCKET_ERROR)
{ //Error }
recvbuf = GlobalAlloc(GMEM_FIXED,dwLength);
if (!recvbuf)
{ //Error }
for (i=0,i<dwCount;i++)
{
dwSenderSize = sizeof(sender);
ret = recvfrom(s,recvbuf,dwLength,0,(SOCKADDR*)&sender,&dwSenderSize);
if (ret == SOCKET_ERROR) { //Error }
else if (ret==0) {break;}
else
{
recvbuf[ret]='/0';
printf("[%s] sent me: '%s'/n",inet_ntoa(sender.sin_addr),recvbuf);
}
}
closesocket(s);
GlobalFree(recvbuf);
WSACleanup();
return 0;
}
ii>无连接的 发送端
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_PORT 5150
#define DEFAULT_COUNT 25
#define DEFAULT_CHAR 'a'
#define DEFAULT_BUFFER_LENGTH 64
BOOL bConnect = FALSE;
int iPort = DEFAULT_PORT;
char cChar = DEFAULT_CHAR;
DWORD dwCount = DEFAULT_COUNT;
dwLength = DEFAULT_BUFFER_LENGTH;
char szRecipient[128];
void usage()
{
printf("");
printf("");
printf("");
printf("");
printf("");
printf("");
printf("");
ExitProcess(1);
}
void ValidateArgs(int argc,char ** argv)
{
int i;
for(i=1;i<argc;i++)
{
if((argv[i][0]=='-')||(argv[i][0]=='/'))
{
switch (tolower(argv[i][1]))
{
case 'p':
if(strlen(argv[i])>3)
iPort = atoi(&argv[i][3]);
break;
case 'r':
if(strlen(argv[i])>3)
strcpy(szRecipient,&argv[i][3]);
break;
case 'c':
if(strlen(argv[i])>3)
dwCount = atoi(&argv[i][3]);
break;
case 'b':
if(strlen(argv[i])>3)
dwLength = atoi(&argv[i][3])
break;
default:
usage();
break;
}
}
}
}
int main(int argc,char** argv)
{
WSADATA wsd;
SOCKET s;
char *sendbuf = NULL;
int ret,
i;
SOCKADDR_IN recipient;
validateArgs(argc,argv);
if (WSAStarup(MAKEWORD(2,2),&wsd)!=0)
{ //Error }
s = socket(AF_INET,SOCK_DGRAM,0);
if(s==INVALID_SOCKET) { //Error }
recipient.sin_family = AF_INET;
recipient.sin_port = htons((short)iProt);
if ((recipient.sin_addr.s_addr = inet_addr(szRecipient))==INADDR_NONE)
{
struct hostent *hosts=NULL;
host = gethostbyname(szRecipient);
if(host)
CopyMemory(&recipient.sin_addr,host->h_addr_list[0],host->h_length);
else
{
printf("gethostbyname() failed: %d/n",WSAGetLastError());
WSAClearup();
return 1;
}
}
sendbuf = GlobalAlloc(GMEM_FIXED,dwLength);
if(!sendbuf)
{ //Error }
memset(sendbuf,cChar,dwLength);
if (bConnect)
{
if(connect(s,(SOCKADDR*)&recipient,sizeof(recipient))==SOCKET_ERROR)
{
printf("");
GlobalFree(sendbuf);
WSACleanup();
return 1;
}
for(i=0;i<dwCount;i++)
{
ret = send(s,sendbuf,dwLength,0);
if (ret=SOCKET_ERROR)
{// Error }
else if (ret==0)
break;
}
}
else
{
for(i=0;i<dwCount;i++)
{
ret = sendto(s,sendbuf,dwLength,0,(SOCKADDR*)&recipient,sizeof(recipient));
if (ret == SOCKET_ERROR)
{ //Error }
else if (ret==0)
break;
}
}
closesocket(s);
GlobalFree(sendbuf);
WSACleanup();
return 0;
}
5> 其它 API 函数
i> int getpeername(SOCKET s, //准备连接的套接字
struct sockaddr FAR* name,
int FAR* namelen
);//获得通信方的套接字地址信息,对数据报来说,是连接调用的地址,而不是sendto地址
ii> int getsockname(SOCKET s,
struct sockaddr FAR* name,
int FAR* namelen
); //返回指定套接字的本地接口信息