Socket编程简介

       Socket简介

80年代初,美国政府的高级研究工程机构(ARPA)给加利福尼亚大学Berkeley分校提供了资金,让他们在UNIX操作系统下实现TCP/IP协议。在这个项目中,研究人员为TCP/IP网络通信开发了一个API(应用程序接口)。这个API称为Socket接口(套接字)。今天,SOCKET接口是TCP/IP网络最为通用的API,也是在INTERNET上进行应用开发最为通用的API。

90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即WindowsSockets规范。它是BerkeleySockets的重要扩充,主要是增加了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。WINDOWSSOCKETS规范是一套开放的、支持多种协议的Windows下的网络编程接口。从1991年的1.0版到1995年的2.0.8版,经过不断完善并在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成为Windows网络编程的事实上的标准。目前,在实际应用中的WINDOWSSOKCETS规范主要有1.1版和2.0版。两者的最重要区别是1.1版只支持TCP/IP协议,而2.0版可以支持多协议。2.0版有良好的向后兼容性,任何使用1.1版的源代码,二进制文件,应用程序都可以不加修改地在2.0规范下使用。

SOCKET实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有SOCKET接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个SOCKET接口来实现。在应用开发中就像使用文件句柄一样,可以对SOCKET句柄进行读,写操作。

 Socket的机制是什么?


我们可以简单的把Socket理解为一个可以连通网络上不同计算机程序之间的管道,把一堆数据从管道的A端扔进去,则会从管道的B端(也许同时还可以从C、D、E、F……端冒出来)。管道的端口由两个因素来唯一确认,即机器的IP地址和程序所使用的端口号。IP地址的含义所有人都知道,所谓端口号就是程序员指定的一个数字,许多著名的木马程序成天在网络上扫描不同的端口号就是为了获取一个可以连通的端口从而进行破坏。比较著名的端口号有http的80端口和ftp的21端口(我记错了么?)。当然,建议大家自己写程序不要使用太小的端口号,它们一般被系统占用了,也不要使用一些著名的端口,一般来说使用1000~5000之内的端口比较好。

Socket可以支持数据的发送和接收,它会定义一种称为套接字的变量,发送数据时首先创建套接字,然后使用该套接字的sendto等方法对准某个IP/端口进行数据发送;接收端也首先创建套接字,然后将该套接字绑定到一个IP/端口上,所有发向此端口的数据会被该套接字的recv等函数读出。如同读出文件中的数据一样。

  所需的头文件、库文件和DLL


对于目前使用最广泛的Windows Socket2.0版本,所需的一些文件如下(以安装了VC6为例说明其物理位置):

        头文件winsock2.h,通常处于C:"Program Files"Microsoft Visual Studio"VC98"INCLUDE;查看该头文件可知其中又包含了windows.h和pshpack4.h头文件,因此在windows中的一些常用API都可以使用;

        库文件Ws2_32.lib,通常处于C:"Program Files"Microsoft Visual Studio"VC98"Lib;

        DLL文件Ws2_32.dll,通常处于C:"WINDOWS"system32,这个是可以猜到的。

 

4.    Socket库内比较重要的几个函数和数据类型

4.1 数据类型

SOCKET类型

SOCKET是socket套接字类型,在WINSOCK2.H中有如下定义:

typedef unsigned int    u_int;

typedef u_int           SOCKET;

可知套接字实际上就是一个无符号整型,它将被Socket环境管理和使用。套接字将被创建、设置、用来发送和接收数据,最后会被关闭。

 

WORD类型、MAKEWORD、LOBYTE和HIBYTE宏

WORD类型是一个16位的无符号整型,在WTYPES.H中被定义为:

typedef unsigned short WORD;

其目的是提供两个字节的存储,在Socket中这两个字节可以表示主版本号和副版本号。使用MAKEWORD宏可以给一个WORD类型赋值。例如要表示主版本号2,副版本号0,可以使用以下代码:

WORD wVersionRequested;

wVersionRequested = MAKEWORD( 2, 0 ); 

注意低位内存存储主版本号2,高位内存存储副版本号0,其值为0x0002。使用宏LOBYTE可以读取WORD的低位字节,HIBYTE可以读取高位字节。

 

sockaddr_in、in_addr类型

sockaddr_in定义了socket发送和接收数据包的地址,定义:

struct sockaddr_in {

        short   sin_family;

        u_short sin_port;

        struct in_addr sin_addr;

        char    sin_zero[8];

};

其中in_addr的定义如下:

struct in_addr {

        union {

                struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;

                struct { u_short s_w1,s_w2; } S_un_w;

                u_long S_addr;

        } S_un;

首先阐述in_addr的含义,很显然它是一个存储ip地址的联合体(忘记union含义的请看c++书),有三种表达方式:

第一种用四个字节来表示IP地址的四个数字;

第二种用两个双字节来表示IP地址;

第三种用一个长整型来表示IP地址。

给in_addr赋值的一种最简单方法是使用inet_addr函数,它可以把一个代表IP地址的字符串赋值转换为in_addr类型,如

addrto.sin_addr.s_addr=inet_addr("192.168.0.2");

本例子中由于是广播地址,所以没有使用这个函数。其反函数是inet_ntoa,可以把一个in_addr类型转换为一个字符串。

sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下:

第一个字段short   sin_family,代表网络地址族,如前所述,只能取值AF_INET;

第二个字段u_short sin_port,代表IP地址端口,由程序员指定;

第三个字段struct in_addr sin_addr,代表IP地址;

第四个字段char    sin_zero[8],很搞笑,是为了保证sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段。

以下代表指明了广播地址,端口号为7861的一个地址:

    sockaddr_in addrto;            //发往的地址 

    memset(&addrto,0,sizeof(addrto));

    addrto.sin_family = AF_INET;               //地址类型为internetwork

    addrto.sin_addr.s_addr = INADDR_BROADCAST; //设置ip为广播地址

    addrto.sin_port = htons(7861);             //端口号为7861

 

sockaddr类型

sockaddr类型是用来表示Socket地址的类型,同上面的sockaddr_in类型相比,sockaddr的适用范围更广,因为sockaddr_in只适用于TCP/IP地址。Sockaddr的定义如下:

struct sockaddr {

 u_short    sa_family;

 char       sa_data[14];

};  

可知sockaddr有16个字节,而sockaddr_in也有16个字节,所以sockaddr_in是可以强制类型转换为sockaddr的。事实上也往往使用这种方法。

 

WSADATA类型和LPWSADATA类型

WSADATA类型是一个结构,描述了Socket库的一些相关信息,其结构定义如下:

typedef struct WSAData {

        WORD                    wVersion;

        WORD                    wHighVersion;

        char                    szDescription[WSADESCRIPTION_LEN+1];

        char                    szSystemStatus[WSASYS_STATUS_LEN+1];

        unsigned short          iMaxSockets;

        unsigned short          iMaxUdpDg;

        char FAR *              lpVendorInfo;

} WSADATA;

typedef WSADATA FAR *LPWSADATA;

值得注意的就是wVersion字段,存储了Socket的版本类型。LPWSADATA是WSADATA的指针类型。它们不用程序员手动填写,而是通过Socket的初始化函数WSAStartup读取出来。

4.2函数

WSAStartup函数

WSAStartup函数被用来初始化Socket环境,它的定义如下:

int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);

其返回值为整型,调用方式为PASCAL(即标准类型,PASCAL等于__stdcall),参数有两个,第一个参数为WORD类型,指明了Socket的版本号,第二个参数为WSADATA类型的指针。

若返回值为0,则初始化成功,若不为0则失败。

 

WSACleanup函数

这是Socket环境的退出函数。返回值为0表示成功,SOCKET_ERROR表示失败。

 

WSAGetLastError函数

该函数用来在Socket相关API失败后读取错误码,根据这些错误码可以对照查出错误原因。

 

socket函数

socket的创建函数,其定义为:

SOCKET PASCAL FAR socket (int af, int type, int protocol);

第一个参数为int af,代表网络地址族,目前只有一种取值是有效的,即AF_INET,代表internet地址族;

第二个参数为int type,代表网络协议类型,SOCK_DGRAM代表UDP协议,SOCK_STREAM代表TCP协议;

第三个参数为int protocol,指定网络地址族的特殊协议,目前无用,赋值0即可。

返回值为SOCKET,若返回INVALID_SOCKET则失败。

 

setsockopt函数

这个函数用来设置Socket的属性,若不能正确设置socket属性,则数据的发送和接收会失败。定义如下:

int PASCAL FAR setsockopt (SOCKET s, int level, int optname,

                           const char FAR * optval, int optlen);

其返回值为int类型,0代表成功,SOCKET_ERROR代表有错误发生。

第一个参数SOCKET s,代表要设置的套接字;

第二个参数int level,代表要设置的属性所处的层次,层次包含以下取值:SOL_SOCKET代表套接字层次;IPPROTO_TCP代表TCP协议层次,IPPROTO_IP代表IP协议层次(后面两个我都没有用过);

第三个参数int optname,代表设置参数的名称,SO_BROADCAST代表允许发送广播数据的属性,其它属性可参考MSDN;

第四个参数const char FAR * optval,代表指向存储参数数值的指针,注意这里可能要使用reinterpret_cast类型转换;

第五个参数int optlen,代表存储参数数值变量的长度。

Sleep函数

线程挂起函数,表示线程挂起一段时间。Sleep(1000)表示挂起一秒。定义于WINBASE.H头文件中。WINBASE.H又被包含于WINDOWS.H中,然后WINDOWS.H被WINSOCK2.H包含。所以在本例中使用Sleep函数不需要包含其它头文件。

 

sendto函数

在Socket中有两套发送和接收函数,一是sendto和recvfrom;二是send和recv。前一套在函数参数中要指明地址;而后一套需要先将套接字和一个地址绑定,然后直接发送和接收,不需绑定地址。sendto的定义如下:

int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR *to, int tolen);

第一个参数就是套接字;

第二个参数是要传送的数据指针;

第三个参数是要传送的数据长度(字节数);

第四个参数是传送方式的标识,如果不需要特殊要求则可以设置为0,其它值请参考MSDN;

第五个参数是目标地址,注意这里使用的是sockaddr的指针;

第六个参数是地址的长度;

返回值为整型,如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。

 

recvfrom函数

int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR *from, int FAR *fromlen);

第一个参数就是套接字;

第二个参数是要接收的数据指针;

第三个参数是要接收的数据长度(字节数);

第四个参数是传送方式的标识,如果不需要特殊要求则可以设置为0,其它值请参考MSDN;

第五个参数是接收地址,注意这里使用的是sockaddr的指针;

第六个参数是指向接收缓冲区长度的指针;

返回值为整型,如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。

 

Sleep函数

线程挂起函数,表示线程挂起一段时间。Sleep(1000)表示挂起一秒。定义于WINBASE.H头文件中。WINBASE.H又被包含于WINDOWS.H中,然后WINDOWS.H被WINSOCK2.H包含。所以在本例中使用Sleep函数不需要包含其它头文件。

 

closesocket

关闭套接字,其参数为SOCKET类型。成功返回0,失败返回SOCKET_ERROR。

bind函数

bind函数用来将一个套接字绑定到一个IP地址。一般只在服务方(即数据发送方)调用,很多函数会隐式的调用bind函数。

 

bind函数

bind函数用来将一个套接字绑定到一个IP地址。一般只在服务方(即数据发送方)调用,很多函数会隐式的调用bind函数。

 

listen函数

从服务方监听客户方的连接。同一个套接字可以多次监听。

 

connect和accept函数

connect是客户方连接服务方的函数,而accept是服务方同意客户方连接的函数。这两个配套函数分别在各自的程序中被成功调用后就可以收发数据了。

 

send和recv函数

send和recv是用来发送和接收数据的两个重要函数。send只能在已经连接的状态下使用,而recv可以面向连接和非连接的状态下使用。

send的定义如下:

int WSAAPI send(

    SOCKET s,

    const char FAR * buf,

    int len,

    int flags

    );

其参数的含义和sendto中的前四个参数一样。而recv的定义如下:

int WSAAPI recv(

    SOCKET s,

    char FAR * buf,

    int len,

    int flags

    );

其参数含义与send中的参数含义一样。


 5、实例
5.1UDP实例
UDP发送程序的步骤如下:

1.         用WSAStartup函数初始化Socket环境;

2.         用socket函数创建一个套接字;

3.         用setsockopt函数设置套接字的属性,例如设置为广播类型;很多时候该步骤可以省略;

4.         创建一个sockaddr_in,并指定其IP地址和端口号;

5.         用sendto函数向指定地址发送数据,这里的目标地址就是广播地址;注意这里不需要绑定,即使绑定了,其地址也会被sendto中的参数覆盖;若使用send函数则会出错,因为send是面向连接的,而UDP是非连接的,只能使用sendto发送数据;

6.         用closesocket函数关闭套接字;

7.         用WSACleanup函数关闭Socket环境。

 

    UDP接收程序的步骤如下(注意接收方一定要bind套接字):

1.         用WSAStartup函数初始化Socket环境;

2.         用socket函数创建一个套接字;

3.         用setsockopt函数设置套接字的属性,例如设置为广播类型;

4.         创建一个sockaddr_in,并指定其IP地址和端口号;

5.         用bind函数将套接字与接收的地址绑定起来,然后调用recvfrom函数或者recv接收数据;注意这里一定要绑定,因为接收报文的套接字必须在网络上有一个绑定的名称才能保证正确接收数据;

6.         用closesocket函数关闭套接字;

7.         用WSACleanup函数关闭Socket环境。

 

发送端程序 

#include <winsock2.h>
#include <iostream.h>

void main()
{
 SOCKET sock;   //socket套接字
 char szMsg[] = "this is a UDP test package";//被发送的字段
 
 //1.启动SOCKET库,版本为2.0
 WORD wVersionRequested;
 WSADATA wsaData;
 int err; 
 wVersionRequested = MAKEWORD( 2, 0); 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( 0 != err )  //检查Socket初始化是否成功
 {
  cout<<"Socket2.0初始化失败,Exit!";
  return;
 }
 //检查Socket库的版本是否为2.0
 if (LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
 {
  WSACleanup( );
  return;
 }
 
 //2.创建socket,
 sock = socket(
  AF_INET,           //internetwork: UDP, TCP, etc
  SOCK_DGRAM,        //SOCK_DGRAM说明是UDP类型
                   //protocol
  );
 
 if (INVALID_SOCKET == sock ) {
  cout<<"Socket 创建失败,Exit!";
  return;
 }
 
 //3.设置该套接字为广播类型,
 bool opt = true;
 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char FAR *>(&opt), sizeof(opt));
 
 //4.设置发往的地址
 sockaddr_in addrto;            //发往的地址 
 memset(&addrto,0,sizeof(addrto));
 addrto.sin_family = AF_INET;               //地址类型为internetwork
    addrto.sin_addr.s_addr = INADDR_BROADCAST; //设置ip为广播地址
 addrto.sin_port = htons(7861);             //端口号为7861
 
 int nlen=sizeof(addrto);
 unsigned int uIndex = 1;
 while(true)
 {
  Sleep(1000);  //程序休眠一秒
  //向广播地址发送消息
  if( sendto(sock, szMsg, strlen(szMsg), 0, reinterpret_cast<sockaddr*>(&addrto),nlen)
   == SOCKET_ERROR )
   cout<<WSAGetLastError()<<endl;
  else
   cout<<uIndex++<<":an UDP package is sended."<<endl;
 }
 
 if (!closesocket(sock))  //关闭套接字
 {
  WSAGetLastError();
  return;
 }
 if (!WSACleanup())       //关闭Socket库
 {
  WSAGetLastError();
  return;
 
}

 

接收端程序:

#include <winsock2.h>
#include <iostream.h>

void main(void)
{
 SOCKET sock;
 
 //1.启动SOCKET库,版本为2.0
 WORD wVersionRequested;
 WSADATA wsaData;
 int err; 
 wVersionRequested = MAKEWORD( 2, 0 ); 
 err = WSAStartup( wVersionRequested, &wsaData );
 if (0 != err)
 {
  cout<<"Socket2.0初始化失败,Exit!";
  return;
 
 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
 {
  WSACleanup();
  return;
 }
 
 //2.创建套接字
 sock = socket(AF_INET,SOCK_DGRAM,0);
 if (INVALID_SOCKET == sock)
 {
  cout<<"Socket 创建失败,Exit!";
  return;
 }

 //3.设置该套接字为广播类型,
 bool opt=true;
 setsockopt(sock,SOL_SOCKET, SO_BROADCAST,(char FAR *)&opt,sizeof(opt));
 
 //4.创建地址
 sockaddr_in from;
 memset(&from,0,sizeof(from));
 from.sin_family=AF_INET;
 //如果绑定地址不是本机地址或者ADDR_ANY,则recvfrom函数不会正确接收,而是立刻返回
 // from.sin_addr.s_addr = ADDR_ANY; 
 from.sin_addr.s_addr = inet_addr("192.168.0.7");
 //端口号必须和客户发往的端口号一致
 from.sin_port=htons(7861);
 
 //5.绑定接收地址
  bind(sock,(sockaddr*)&from,sizeof(from));
 
 memset(&from,0,sizeof(from));
 int fromlength = sizeof(SOCKADDR);
 char buf[256]; 
 memset(buf,0,sizeof(buf));
 long number = 0;
 while(true){
  number++;
 //  recvfrom(sock,buf,256,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength);
   recv(sock,buf,256,0);
   Sleep(1000);
  cout<<number<<":"<<buf<<endl;
  memset(buf,0,sizeof(buf));
 }
 
 if (!closesocket(sock)) {
  WSAGetLastError();
  return;
 }
 if (!WSACleanup()) {
  WSAGetLastError();
  return;
 }
}

5.2UDP实例

TCP与UDP最大的不同之处在于TCP是一个面向连接的协议,在进行数据收发之前TCP必须进行连接,并且在收发的时候必须保持该连接。

发送方的步骤如下(省略了Socket环境的初始化、关闭等内容):

1.         用socket函数创建一个套接字sock;

2.         用bind将sock绑定到本地地址;

3.         用listen侦听sock套接字;

4.         用accept函数接收客户方的连接,返回客户方套接字clientSocket;

5.         在客户方套接字clientSocket上使用send发送数据;

6.         用closesocket函数关闭套接字sock和clientSocket;

而接收方的步骤如下:

1.         用socket函数创建一个套接字sock;

2.         创建一个指向服务方的远程地址;

3.         用connect将sock连接到服务方,使用远程地址;

4.         在套接字上使用recv接收数据;

5.         用closesocket函数关闭套接字sock;

值得注意的是,在服务方有两个地址,一个是本地地址myaddr,另一个是目标地址addrto。本地地址myaddr用来和本地套接字sock绑定,目标地址被sock用来accept客户方套接字clientSocket。这样sock和clientSocket连接成功,这两个地址也连接上了。在服务方使用clientSocket发送数据,则会从本地地址传送到目标地址。

在客户方只有一个地址,即来源地址addrfrom。这个地址被用来connect远程的服务方套接字,connect成功则本地套接字与远程的来源地址连接了,因此可以使用该套接字接收远程数据。其实这时客户方套接字已经被隐性的绑定了本地地址,所以不需要显式调用bind函数,即使调用也不会影像结果。

 

发送端程序 

#include <stdlib.h>
#include <winsock2.h>
#include <iostream.h>

void main()
{
 SOCKET sock, clientSocket;                   //socket
 char szMsg[] = "this is a UDP test package";//被发送的字段
 
 //1.启动SOCKET库,版本为2.0
 WORD wVersionRequested;
 WSADATA wsaData;
 int err; 
 wVersionRequested = MAKEWORD( 2, 0 ); 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 )
 {
  cout<<"Socket2.0初始化失败,Exit!";
  return;
 
 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
 {
  WSACleanup( );
  return;
 }
 
 //2.创建socket,
 sock = socket(
  AF_INET,           //internetwork: UDP, TCP, etc
  SOCK_STREAM,        //SOCK_STREAM说明是TCP socket
                   //protocol
  );
 
 if (sock == INVALID_SOCKET )
 {
  cout<<"Socket 创建失败,Exit!";
  return;
 }
 
 //3.bind套接字,相当于给本地套接字赋值
 sockaddr_in myaddr;
 memset(&myaddr,0,sizeof(myaddr));
 myaddr.sin_family=AF_INET;

 myaddr.sin_addr.s_addr = inet_addr("192.168.0.7");
 myaddr.sin_port=htons(7861);
 bind(sock,(sockaddr*)&myaddr,sizeof(myaddr));

 //4.设置发往的地址
 sockaddr_in addrto;            //发往的地址 
 memset(&addrto,0,sizeof(addrto));
 addrto.sin_family=AF_INET;
    addrto.sin_addr.s_addr=inet_addr("192.168.0.9");
 //端口号必须和服务器绑定的端口号一致
 addrto.sin_port=htons(7861);
 
 //5.listen 另一端的socket
 if (listen(sock,5) == SOCKET_ERROR )
 {
  closesocket(sock);
  WSACleanup();
  abort();
 }
 else
 {
  cout<<"listen succeed!"<<endl;
 }
 
 //6. accept 对方连接
 int nlen = sizeof(addrto);
 clientSocket = accept(sock,(sockaddr*)&addrto,&nlen);
 if (clientSocket < 0)
 {
  cout<<"client socket error, exit!"<<endl;
  abort();
 }
 else
 {
  cout<<"accepted client socket!"<<endl;
 }
 
 unsigned int uIndex = 1;
 while(true)
 {
  Sleep(1000);

  if( send(clientSocket, szMsg, strlen(szMsg), 0) == SOCKET_ERROR )
   cout<<"error, error id = "<<WSAGetLastError()<<endl;
  else
   cout<<uIndex++<<":an UDP package is sended."<<endl;
 }
 
 if (!closesocket(sock))
 {
  WSAGetLastError();
  return;
 }
 if (!closesocket(clientSocket))
 {
  WSAGetLastError();
  return;
 }
 if (!WSACleanup())
 {
  WSAGetLastError();
  return;
 
}

接收端程序:

#include <winsock2.h>
#include <iostream.h>
#include <stdlib.h>

void main(void)
{
 SOCKET sock;
 
 //1.启动SOCKET库,版本为2.0
 WORD wVersionRequested;
 WSADATA wsaData;
 int err; 
 wVersionRequested = MAKEWORD( 2, 0 ); 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 )
 {
  cout<<"Socket2.0初始化失败,Exit!";
  return;
 
 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
 {
  WSACleanup();
  return;
 }

 //2.创建套接字
 sock = socket(AF_INET,SOCK_STREAM,0);
 if (sock == INVALID_SOCKET ) {
  cout<<"Socket 创建失败,Exit!";
  return;
 }

 //3.定义地址
 sockaddr_in myaddr;
 memset(&myaddr,0,sizeof(myaddr));
 myaddr.sin_family=AF_INET;
 
 myaddr.sin_addr.s_addr = inet_addr("192.168.0.9"); 
 //端口号必须和客户发往的端口号一致
 myaddr.sin_port=htons(7861);

 //4.connect
 if (connect(sock,(sockaddr*)&myaddr,sizeof(myaddr)) == SOCKET_ERROR)
 {
  cout<<"connect failed, exit!"<<endl;
  closesocket(sock);
  WSACleanup();
  exit(1);
 }
 else
 {
  cout<<"connect succeed!"<<endl;
 }
 
 char buf[256]=""; 
 long number=0;
 int iResult = 0;
 while(true)
 {
  number++;
  iResult = recv(sock,buf,sizeof(buf),0);
  if (iResult < 0)
  {
   cout<<"服务方Socket关闭,退出!"<<endl;
   break;
  }
  Sleep(1000);
  cout<<number<<":"<<buf<<endl;
  memset(buf,0,sizeof(buf));
 }

 if (!closesocket(sock))
 {
  WSAGetLastError();
  return;
 }
 if (!WSACleanup())
 {
  WSAGetLastError();
  return;
 }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值