[转]Winsock程序设计初步之 Winsock编程原理

 
Winsock程序设计初步之 Winsock编程原理
 
  本课程主要讲Windows中TCP
/ IP编程接口Winsock,版本为1. 1 。高版本的Winsock实际与1.1版相差不多,主要是进行了一些扩充,如可超越TCP / IP协议直接用socket来实现IPX、NETBIOS等其它通信协议。

  这叙述方便在本文的其余部分中提到的Winsock指的就是Winsock1.
1

  通过Winsock可实现点对点或广播通信程序,实际这两者之间的区别不大,编程时其程序流程所用代码几乎相同,不同的地方在于目标地址选择的不同。本课程中所举实例为点对点的形式,并以客户
/ 服务器形式来构建通过Winsock进行通信的点对点通信,并对通信过程的两点分别命名为Server和 Client。

为更清楚的说明出Winsock的结构原理,下面以电信局的普通电话服务为比较对象进行说明:
1 、电信局提供电话服务类似版主们这的Server,普通电话用户类似版主们这的Client。

2 、首先电信局必须建立一个电话总机。这就如果版主们必须在Server端建立一个Socket(套接字),这一步通过调用socket()函数实现。

3 、电信局必须给电话总机分配一个号码,以便使用户要拨找该号码得到电话服务,同时接入该电信局的用户必须知道该总机的号码。同样,版主也在Server 端也要为这一套接字指定一port(端口),并且要连接该Server的Client必须知道该端口。这一步通过调用bind()函数实现。

4 、接下来电信局必须使总机开通并使总机能够高效地监听用户拨号,如果电信局所提供服务的用户数太多,你会发现拨打电信局总机老是忙音,通常电信局内部会使该总机对应的电话号码连到好几个负责交换的处理中心,在一个处理中心忙于处理当前的某个用户时,新到用户可自动转到一下处理中心得到服务。同样版主们的 Server端也要使自己的套接口设置成监听状态,这是通用listen()函数实现的,listen()的第二个参数是等待队列数,就如同你可以指定电信局的建立几个负责交换的处理中心。

5 、用户知道了电信局的总机号后就可以进行拨打请求得到服务。在Winsock的世界里做为Client端是要先用socket()函数建立一个套接字,然后调connect()函数进行连接。当然和电话一样,如果等待队列数满了、与Server的线路不通或是Server没有提供此项服务时,连接就不会成功。

5 、电信局的总机接受了这用户拨打的电话后负责接通用户的线路,而总机本身则再回到等待的状态。Server也是一样,调用accept()函数进入监听处理过程,Server端的代码即在中处暂停,一旦Server端接到申请后系统会建立一个新的套接字来对此连接做服务,而原先的套接字则再回到监听等待的状态。

6 、当你电话挂完了,你就可以挂上电话,彼此间也就离线了。Client和Server间的套接字的关闭也是如此;这个关闭离线的动作,可由Client端或Server端劝嬷骰方先关闭。有些电话查询系统不也是如此吗?关闭套接字的函数为
closesocket()。

从以上情况可以看出在服务器端建立一个套接字,并进入实际的监听步骤的过程如下:socket()
-> bind() -> listen() -> accept()

那么在accept()完了后,版主们说在Server端将生成一个新的套接字,然后Server将继续进入accept()状态,版主们该如何用这个新的套接字来进行与Client端的通信呢,这就用到了recv()函数,而Client端则是通过send()函数来向服务器发信息的。

在客户端也是采取类似的过程,其调用Winsock的过程如下:
socket()
-> connect() -> send()
首先建立一个socket,然后用connect()函数将其与Server端的socket连接,连接成功后调用send()发送信息。

// A simplest web server
// Written by Shen zhiliang for learning Winsock & HTTP
file: // zhiliang@sina.com
 
#include 
" winsock.h "
#include 
" stdio.h "
#include 
" conio.h "
#include 
" io.h "

#define  BUFLEN 2048
#define  DEFPATH ("C:/frontpage webs/content/")
#define  DEFFILE ("INDEX.HTM")
#define  HTTPHEAD ("HTTP/1.0 200 OKDate: Monday, 04-Jan-99 17:06:17 GMT Server: HTTP-Server/1.0  MIME-version: 1.0 ")
#define  MIMEHTML ("Content-type: text/html  Last-modified: Friday, 26-Sep-97 09:36:54 GMT ")
#define  MIMEGIF ("Content-type: /image/gif  Last-modified: Friday, 26-Sep-97 09:36:54 GMT ")
#define  MIMEJPEG ("Content-type: /image/jpeg  Last-modified: Friday, 26-Sep-97 09:36:54 GMT ")
#define  MIMEPAIN ("Content-type: text/pain  Last-modified: Friday, 26-Sep-97 09:36:54 GMT ")
#define  HTMLHEAD ("<html><body> ")
#define  HTMLTAIL (" </body></html>")

void  P( char   *  a)
{
    printf(
"Error in : %s ",a);
}


/*
char * httphead(FILE * fp)
{
    struct tm *newtime;
    time_t aclock;
    time( &aclock );
    newtime = localtime( &aclock );
    printf( "The current date and time are: %s", asctime( newtime ) );
}
*/


void  HtmlError(SOCKET s,unsigned  int  code, char   *  msg)
{
    
char tmp[1024];
    sprintf(tmp,
"Error code: %d %s",code,msg);
    send(s,HTTPHEAD,strlen(HTTPHEAD),
0);
    send(s,HTMLHEAD,strlen(HTMLHEAD),
0);
    send(s,tmp,strlen(tmp),
0);
    send(s,HTMLTAIL,strlen(HTMLTAIL),
0);
}


void  SendHtmlFile(SOCKET s, char   *  filename)
{
    FILE 
* fp;
    
char * tmp;
    
char fullname[512];
    
char buf[1024];
    
int i;
 
    strcpy(fullname,DEFPATH);
 
    
if(strlen(filename)==0||(strlen(filename)==1&&filename[0]=='/'))
        strcat(fullname,DEFFILE);
    
else
    
{
        
do{
            tmp
=strchr(filename,'/');
            
if(tmp!=NULL)
                tmp[
0]='/';
        }
while(tmp!=NULL);

        
if(filename[0]=='/')
            strcat(fullname,
&filename[1]);
        
else
            strcat(fullname,filename);
    }


    FILE 
* fpt=fopen("recv.dat","a+b");
    fwrite(fullname,
sizeof(char),strlen(fullname),fpt);
    fclose(fpt);
    fp
=fopen(fullname,"rb");
 
    
if(fp==NULL)
    
{
        
char msg[512];
        
if(filename[0]=='/')
            filename[
0]='/';
        sprintf(msg,
" URI no found: %s",filename);
        HtmlError(s,
404,msg);
        
return;
    }


    send(s,HTTPHEAD,strlen(HTTPHEAD),
0);
    
if(stricmp(&filename[strlen(filename)-4],".GIF")==0)
        send(s,MIMEGIF,strlen(MIMEGIF),
0);
    
else if(stricmp(&filename[strlen(filename)-4],".JPG")==0|| stricmp(&filename[strlen(filename)-5],".JPEG")==0)
        send(s,MIMEJPEG,strlen(MIMEJPEG),
0);
    
else if(stricmp(&filename[strlen(filename)-4],".HTM")==0|| stricmp(&filename[strlen(filename)-5],".HTML")==0)
    
{
        send(s,MIMEHTML,strlen(MIMEHTML),
0);
    }

 
    
long fs=_filelength(_fileno(fp));
    buf[
0]=0;
    sprintf(buf,
"Content-length: %ld ",fs);
    send(s,buf,strlen(buf),
0);
    
for(i=0;!feof(fp);i++)
    
{
        buf[i]
=fgetc(fp);
        
if(i>=1023||feof(fp))
        
{
            send(s,buf,i,
0);
            i
=0;
        }

    }


    fclose(fp);
}


void  SocketError( char   *  call)
{
    fprintf(stderr,
" WinSock Error# function: %s, error code:%ld ",call,WSAGetLastError());
}


main(
int  argc, char   **  argv)
{
    
int iRes,iPort,iTmp;
    SOCKET s,rs;
    SOCKADDR_IN sin,rsin;
    WSADATA wsad;
    WORD wVersionReq;
    
char recvBuf[BUFLEN];
 
    
if(argc<2)
    
{
        fprintf(stderr,
"Usage: WebServer 999 999 - Port number for this server.");
        
return -1;
    }


    iPort
=0;
    iPort
=atoi(argv[1]);
    
if(iPort<=0)
    
{
        fprintf(stderr,
"must specify a port number");
        
return -1;
    }

    wVersionReq
=MAKEWORD(1,1);
 
    iRes
=WSAStartup(wVersionReq,&wsad);
    
if(iRes!=0)
    
{
        SocketError(
"WSAStartup()");
        
return -1;
    }

 
    s
=socket(PF_INET,SOCK_STREAM,0);
    
if(s==INVALID_SOCKET)
    
{
        SocketError(
"socket()");
        
return -1;
    }


    sin.sin_family
=PF_INET;
    sin.sin_port
=htons(iPort);
    sin.sin_addr.s_addr
=INADDR_ANY;
    iTmp
=sizeof(sin);

    
if(bind(s,(LPSOCKADDR)&sin,iTmp)==SOCKET_ERROR)
    
{
        SocketError(
"bind()");
        closesocket(s);
        WSACleanup();
        
return -1;
    }

 
    
if(listen(s,1)==SOCKET_ERROR)
    
{
        SocketError(
"listen()");
        closesocket(s);
        WSACleanup();
        
return -1;
    }


    fprintf(stderr,
"WebServer Running...... ");
    iTmp
=sizeof(rsin);
    rs
=0;
    
while(1)
    
{
        
if(_kbhit()!=0)
        
{
            
if(_getch()!=27)
                
break;
            
if(rc!=0)
            closesocket(rs);
            closesocket(s);
            WSACleanup();
            fprintf(stderr,
"WebServer Stopped...... ");
            
return 0;
        }


        rs
=accept(s,(LPSOCKADDR)&rsin,&iTmp);
        
if(rs==INVALID_SOCKET)
        
{
            SocketError(
"accept()");
            closesocket(s);
            WSACleanup();
            
return -1;
        }


        iRes
=recv(rs,recvBuf,BUFLEN,0);
        printf(
"RECEIVED DATA:  --------------------------------- %s --------------------------------- ",recvBuf);
        
if(iRes==SOCKET_ERROR)
        
{
            SocketError(
"recv()");
            closesocket(rs);
            closesocket(s);
            WSACleanup();
            
return -1;
        }

 
        
char * sRes;
        sRes
=strstr(recvBuf,"GET");
        
if(sRes!=NULL&&(sRes-recvBuf)<3)
            sRes
=strchr(recvBuf,'/');
        
if(sRes!=NULL)
        
{
            
char * sRes1;
            sRes1
=strchr(sRes,' ');
            
if(strchr(sRes,' ')<sRes1)
                sRes1
=strchr(sRes,' ');
            
if(sRes1!=NULL&&(sRes1-sRes)<256)
            
{
                
char tmp[256];
                strncpy(tmp,sRes,(sRes1
-sRes));
                tmp[sRes1
-sRes]=0;
                
int i;
                
for(i=strlen(tmp)-1;(tmp[i]==' '||tmp[i]==' ')&&i>=0;i--)
                    tmp[i]
=0;
                
for(i=0;tmp[i]==' '||tmp[i]==' ';i++);
                SendHtmlFile(rs,
&tmp[i]);
            }

        }

        
else
        
{
            HtmlError(rs,
202,"Bad request");
        }

        closesocket(rs);
    }


    
return 0;
}



// A simplest web client
// Written by Shen zhiliang for learning Winsock & HTTP
// zhiliang@sina.com
// 1998.7.29

#include 
" winsock.h "
#include 
" stdio.h "

#define  BUFLEN 4096

void  SocketError( char   *  call)
{
    fprintf(stderr,
" WinSock Error# function: %s, error code:%ld ",call,WSAGetLastError());
}


main(
int  argc, char   **  argv)
{
    
int iRes,iPort,iTmp;
    SOCKET s,rs;
    SOCKADDR_IN sin,rsin;
    WSADATA wsad;
    WORD wVersionReq;
    
char recvBuf[BUFLEN];
 
    
if(argc<4)
    
{
        fprintf(stderr,
"Usage: sockserver ip port message ");
        
return -1;
    }

    
if(inet_addr(argv[1])==INADDR_NONE)
    
{
        fprintf(stderr,
"Error ip gaving ");
        
return -1;
    }

    iPort
=0;
    iPort
=atoi(argv[2]);
    sin.sin_addr.s_addr
=inet_addr(argv[1]);
    sin.sin_family
=PF_INET;
    sin.sin_port
=htons(iPort);
    
if(iPort<=0)
    
{
        fprintf(stderr,
"must specify a number for port ");
        
return -1;
    }

    wVersionReq
=MAKEWORD(1,1);
 
    iRes
=WSAStartup(wVersionReq,&wsad);
    
if(iRes!=0)
    
{
        SocketError(
"WSAStartup()");
        
return -1;
    }

 
    s
=socket(PF_INET,SOCK_STREAM,0);
    
if(s==INVALID_SOCKET)
    
{
        SocketError(
"socket()");
        
return -1;
    }

    iTmp
=sizeof(sin);
    fprintf(stderr,
"WinSock Client Start...... ");
    
if(connect(s,(LPSOCKADDR)&sin,iTmp)==SOCKET_ERROR)
    
{
        SocketError(
"connect()");
        closesocket(s);
        WSACleanup();
        
return -1;
    }


    strcpy(recvBuf,argv[
3]);
    strcat(recvBuf,
" ");
    iRes
=send(s,recvBuf,strlen(recvBuf),0);
    
if(iRes==SOCKET_ERROR)
    
{
        SocketError(
"send()");
        closesocket(s);
        WSACleanup();
        
return -1;
    }

    printf(
"Sent Data: ------------------ %s ------------------ ",recvBuf);


    FILE 
* fp=fopen("send.dat","a+b");
    
if(fp==NULL)
        
return -1;
    iRes
=recv(s,recvBuf,BUFLEN,0);
    
while(iRes!=SOCKET_ERROR&&iRes!=0)
    
{
        printf(
"Received Data: ------------------ %s ------------------ ",recvBuf);
        fwrite(recvBuf,
sizeof(char),iRes,fp);
        iRes
=recv(s,recvBuf,BUFLEN,0);
    }

 
    fclose(fp);
    closesocket(s);
    WSACleanup();
    
return 0;
}



Winsock函数用法说明

WSAStartup()
连结应用程序与Winsock.DLL 的第一个函数。
格 式:
  
int  WSAStartup( WORD wVersionRequested,LPWSADATA lpWSAData )
参 数:
  wVersionRequested 欲使用的 Windows Sockets API 版本
  lpWSAData 指向 WSADATA 资料的指标
传回值:
  成功 
-   0
  失败 
-  WSASYSNOTREADY  /  WSAVERNOTSUPPORTED  /  WSAEINVAL
说明:
  此函数「必须」是应用程序呼叫到 Windows Sockets DLL 函数中的第一个函数呼叫成功后,才可以再呼叫其他 Windows Sockets DLL 的函数。此函数亦让使用者可以指定要使用的 Windows Sockets API 版本,及获取设计者的一些信息。

socket()
建立Socket。
格 式:
  SOCKET socket( 
int  af,  int  type,  int  protocol )
参 数:
  af 目前只提供 PF_INET(AF_INET)
  type Socket 的型态 (SOCK_STREAM、SOCK_DGRAM)
  protocol 通讯协定(如果使用者不指定则设为0)
传回值:
  成功 
-  Socket 的识别码
  失败 
-  INVALID_SOCKET(呼叫 WSAGetLastError() 可得知原因)
说明:
  此函数用来建立一 Socket,并为此 Socket 建立其所使用的资源。Socket 的型态可为 Stream Socket 或 Datagram Socket。

bind()
指定 Socket 的 Local 地址 (Address)。
格 式:
  
int  bind( SOCKET s,  const   struct  sockaddr FAR  * name, int  namelen );
参 数:
  s Socket的识别码
  name Socket的地址值
  namelen name的长度
传回值:
  成功 
-   0
  失败 
-  SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明:
  此一函数是指定 Local 地址及 Port 给某一未定名之 Socket。使用者若不在意地址或 Port 的值,那麽他可以设定地址为 INADDR_ANY,及 Port 为 
0 ;那么Windows Sockets 会自动将其设定适当之地址及 Port ( 1024  到 5000之间的值),使用者可以在此 Socket 真正连接完成后,呼叫 getsockname() 来获知其被设定的值。
bind() 函数要指定地址及 port,这个地址必须是执行这个程序所在机器的 IP地址,所以如果读者在设计程序时可以将地址设定为 INADDR_ANY,这样Winsock 系统会自动将机器正确的地址填入。如果您要让程序只能在某台机器上执行的话,那么就将地址设定为该台机器的 IP 地址。由於此端是 Server 端,所以版主们一定要指定一个 port 号码给这个 socket。

listen()
设定 Socket 为监听状态,准备被连接。
格 式:
  
int  listen( SOCKET s,  int  backlog );
参 数:
  s Socket 的识别码
  backlog 未真正完成连接前(尚未呼叫 accept 前)彼端的连接要求的最大个数
传回值:
  成功 
-   0
  失败 
-  SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明:
  使用者可利用此函数来设定 Socket 进入监听状态,并设定最多可有多少个在未真正完成连接前的彼端的连接要求。(目前最大值限制为 
5 , 最小值为1)

connect()
要求连接某一 TCP Socket 到指定的对方。
格 式:
  
int  connect( SOCKET s,  const   struct  sockaddr FAR  * name,  int  namelen );
参 数:
  s Socket 的识别码
  name 此 Socket 想要连接的对方地址
  namelen name的长度
传回值:
  成功 
-   0
  失败 
-  SOCKET_ERROR (呼叫WSAGetLastError()可得知原因)
说明:
  此函数用来向对方要求建立连接。若是指定的对方地址为 
0  的话,会传回错误值。当连接建立完成后,使用者即可利用此一 Socket 来做传送或接收资料之用了。

accept()
接受某一 Socket 的连接要求,以完成 Stream Socket 的连接。
格 式:
  SOCKET accept(SCOKET s, SOCKADDR 
* addr, int  FAR  * addrlen )
参 数:
  s Socket的识别码
  addr 存放来连接的彼端的地址
  addrlen addr的长度
传回值:
  成功 
-  新的Socket识别码
  失败 
-  INVALID_SOCKET (呼叫 WSAGetLastError() 可得知原因)
说明:
  Server 端的应用程序呼叫此一函数来接受 Client 端要求的 Socket 连接动作请求。

closesocket()
关闭某一Socket。
格 式:
  
int  closesocket( SOCKET s );
参 数:
  s Socket 的识别码
传回值:
  成功 
-   0
  失败 
-  SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明:
  此一函数是用来关闭某一 Socket 。

WSACleanup()
结束 Windows Sockets DLL 的使用。
格 式:
  
int  WSACleanup(  void  );
参 数: 无
传回值:
  成功 
-   0
  失败 
-  SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明:
  当应用程序不再需要使用Windows Sockets DLL 时,须呼叫此一函数来注销使用,以便释放其占用的资源。

send()
使用连接式(connected)的 Socket 传送资料。
格 式:
  
int  send( SOCKET s,  const   char  FAR  * buf,  int  len,  int  flags );
参 数:
  s Socket 的识别码
  buf 存放要传送的资料的暂存区
  len buf 的长度
  flags 此函数被呼叫的方式
传回值:
  成功 
-  送出的资料长度
  失败 
-  SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明:
  此函数用于将信息从本端通过socket发送到远程端。

recv()
自 Socket 接收资料。
格 式:
  
int  recv( SOCKET s,  char  FAR  * buf,  int  len,  int  flags );
参 数:
  s Socket 的识别码
  buf 存放接收到的资料的暂存区
  len buf 的长度
  flags 此函数被呼叫的方式
传回值:
  成功 
-  接收到的资料长度 (若对方 Socket 已关闭,则为  0 )
  失败 
-  SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明:
  此函数用来自连接式的 Datagram Socket 或 Stream Socket 接收资料。对 Stream Socket 言,版主们可以接收到目前 input buffer 内有效的资料,但其数量不超过 len 的大小。

WSAStartup()
连结应用程序与 Windows Sockets DLL 的第一个函数。
格 式:
  
int  WSAStartup( WORD wVersionRequested,LPWSADATA lpWSAData );
参 数:
  wVersionRequested 可使用的 Windows Sockets API 最高版本
  lpWSAData 指向 WSADATA 资料的指标
传回值:
  成功 
-   0
  失败 
-  WSASYSNOTREADY  /  WSAVERNOTSUPPORTED  /  WSAEINVAL
说明:
  此函数「必须」是应用程序呼叫到 Windows Sockets DLL 函数中的第一个,也唯有此函数呼叫成功后,才可以再呼叫其他 Windows Sockets DLL 的函数。此函数亦让使用者可以指定要使用的 Windows Sockets API 版本,及获取设计者的一些信息。



【转地址】:http://dev.gameres.com/Program/Control/basesocket.htm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一章 简介 1.1 什么是Windows Sockets规范? Windows Sockets规范以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例定义了一套Micosoft Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。 Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。 遵守这套Windows Sockets规范的网络软件,我们称之为Windows Sockets兼容的,而Windows Sockets兼容实现的提供者,我们称之为Windows Sockets提供者。一个网络软件供应商必须百分之百地实现Windows Sockets规范才能做到现Windows Sockets兼容。 任何能够与Windows Sockets兼容实现协同工作的应用程序就被认为是具有Windows Sockets接口。我们称这种应用程序为Windows Sockets应用程序。 Windows Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要指出的是所有的Windows Sockets实现都支持流套接口和数据报套接口. 应用程序调用Windows Sockets的API实现相互之间的通讯。Windows Sockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值