第一个例子解释_面向连接

1、Winsock的初始化

每个Winsock应用都必须加载Winsock DLL的相应版本。如果调用Winsock之前,没有加载
Winsock库,这个函数就会返回一个SOCKET_ERROR,错误信息是WSANOTINITIALISED。
加载Winsock库是通过调用WSAStartup函数实现的。这个函数的定义如下:
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
wVersionRequested:用于指定准备加载的Winsock库的版本。
  高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。
  可用宏MAKEWORD ( X , Y )(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值。
lpWSAData:指向WSADATA数据结构的指针,用来接受Windows Sockets实现的细节。
            typedef struct WSAData {
                    WORD                    wVersion;     //打算使用的Winsock版本
                    WORD                    wHighVersion; //现有的Winsock库的最高版本
                    char                    szDescription[WSADESCRIPTION_LEN+1]; //没有用
                    char                    szSystemStatus[WSASYS_STATUS_LEN+1]; //没有用
                    unsigned short          iMaxSockets;  //没有用
                    unsigned short          iMaxUdpDg;    //没有用
                    char FAR *              lpVendorInfo; //没有用
            } WSADATA, FAR * LPWSADATA;
返回值
0:成功

注:各windows平台支持的Winsock版本
        平 台   Winsock版本
        Windows 95 1 . 1(2 . 2)
        Windows 98 2 . 2
        Windows NT 4.0 2 . 2
        Windows 2000 2 . 2
        Windows CE 1 . 1

3、服务器API函数

“服务器”其实是一个进程,它需要等待任意数量的客户机连接,以便为它们的请求提供服务。对服务器监听的连接来说,它必须在一个已知的名字上。在T C P / I P中,这个名字就是本地接口的I P地址,加上一个端口编号。在Wi n s o c k中,第一步是将指定协议的套接字绑定到它已知的名字上。这个过程是通过A P I调用b i n d来完成的。下一步是将套接字置为监听模式。这时,用A P I函数l i s t e n来完成的。最后,若一个客户机试图建立连接,服务器必须通过a c c e p t或W S A A c c e p t调用来接受连接。

3.1  bind
一旦为某种特定协议创建了套接字,就必须将套接字绑定到一个已知地址。b i n d函数可将指定的套接字同一个已知地址绑定到一起。该函数声明如下;

int bind(
  SOCKET s,
  const struct sockaddr *name,
  int namelen
);
s:代表我们希望在上面等待客户机连接的那个套接字

name:一个普通的缓冲区 针对自己打算使用的那个协议,必须把该参数实际地填充一个地址缓冲区,并在调用 b i n d时将其造型为一个struct sockaddr

namelen:要传递的、由协议决定的地址的长度

返回值:

如无错误发生,则bind()返回0。否则的话,将返回SOCKET_ERROR,应用程序可通过WSAGetLastError()获取相应错误代码。

例如:

	SOCKET s;
	struct sockaddr_in tcpaddr;
	int port = 5150;
	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	
	tcpaddr.sin_family = AF_INET;
	tcpaddr.sin_port = port;
	tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	bind(s, (struct sockaddr*)&tcpaddr, sizeof(tcpaddr));
在这个例子中,创建了一个流套接字。随后,我们设置了T C P / I P地址结构,打算在它上面接受客户机连接。在这种情况下,套接字绑定到端口号5 1 5 0上的默认I P接口。之后的b i n d调用将套接字同I P接口/端口关联在一起。


3.2 listen
我们接下来要做的是将套接字置入监听模式。b i n d函数的作用只是将一个套接字和一个指定的地址关联在一起。指示一个套接字等候进入连接的A P I函数则是l i s t e n,其定义如下:

int listen(
  SOCKET s,
  int backlog
);
s:代表我们希望在上面等待客户机连接的那个套接字 
backlog:指定了正在等待连接的最大队列长度
backlog参数其实本身就存在着限制,这个限制是由基层的协议提供者决定的。如果出现非法值,那么会用与之最接近的一个合法值来取代。除此以外,对于如何知道实际的
backlog值,其实并不存在一种标准手段。

Return value

If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.


3.3 accept和W S A A c c e p t

SOCKET accept(
  _In_     SOCKET s,
  _Out_    struct sockaddr *addr,
  _Inout_  int *addrlen
);
s:是一个限定套接字,它处在监听模式
addr:一个有效的sockaddr_in结构的地址
addrlen:应该是sockaddr_in结构的长度
对于属于另一种协议的套接字,应当用与那种协议对应的S O C K A D D R结构来替换S O C K A D D R _ I N

a c c e p t函数返回后,a d d r结构中会包含发出连接请求的那个客户机的I P地址信息,而a d d r l e n参数则指出结构的长度。此外,a c c e p t会返回一个新的套接字描述符,它对应于已经接受的那个客户机连接。对于该客户机后续的所有操作,都应使用这个新套接字。至于原来那个监听套接字,它仍然用于接受其他客户机连接,而且仍处于监听模式。

W S A A c c e p t

Winsock 2引入了一个名为W S A A c c e p t的函数。它能根据一个条件函数的返回值,选择性
地接受一个连接。这个新函数的定义如下:

SOCKET WSAAccept(
  _In_     SOCKET s,
  _Out_    struct sockaddr *addr,
  _Inout_  LPINT addrlen,
  _In_     LPCONDITIONPROC lpfnCondition,
  _In_     DWORD_PTR dwCallbackData
);
这个先不学习
4、客户机API函数
4.1 connect
int connect(
  _In_  SOCKET s,
  _In_  const struct sockaddr *name,
  _In_  int namelen
);
s:标识一个未连接套接字的描述字
name:欲进行连接的端口名,针对T C P(说明连接的服务器)的套接字地址结构(S O C K A D D R _ I N)
namelen:名字参数的长度

Winsock 2版本中,它的定义是这样的:
int WSAConnect(
  _In_   SOCKET s,
  _In_   const struct sockaddr *name,
  _In_   int namelen,
  _In_   LPWSABUF lpCallerData,
  _Out_  LPWSABUF lpCalleeData,
  _In_   LPQOS lpSQOS,
  _In_   LPQOS lpGQOS
);
例子,来自MSDN
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

int wmain()
{
    //----------------------
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup function failed with error: %d\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for connecting to server
    SOCKET ConnectSocket;
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET) {
        wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(27015);

    //----------------------
    // Connect to server.
    iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
        iResult = closesocket(ConnectSocket);
        if (iResult == SOCKET_ERROR)
            wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    wprintf(L"Connected to server.\n");

    iResult = closesocket(ConnectSocket);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    WSACleanup();
    return 0;
}
5、数据传输: 
5.1send和W S A S e n d
所有关系到收发数据的缓冲都属于简单的c h a r类型。也就是说,这些函数没有“U n i c o d e”版本。
int send(
  _In_  SOCKET s,
  _In_  const char *buf,
  _In_  int len,
  _In_  int flags
);
s:已建立连接的套接字,将在这个套接字上发送数据
buf:则是字符缓冲区,区内包含即将发送的数据 
len:指定即将发送的缓冲区内的字符数
flags:可为0、M S G _ D O N T R O U T E或M S G _ O O B
f l a g s还可以是对那些标志进行按位“或运算”的一个结果。M S G _ D O N T R O U T E标志要求传送层不要将它发出的包路由出去。由基层的传送决定是否实现这一请求(例如,若传送协议不支持该选项,这一请求
就会被忽略)。M S G _ O O B标志预示数据应该被带外发送。
返回值:
s e n d返回发送的字节数;若发生错误,就返回S O C K E T _ E R R O R
send API函数的Winsock 2版本是W S A S e n d,它的定义如下:
int WSASend(
  _In_   SOCKET s,
  _In_   LPWSABUF lpBuffers,
  _In_   DWORD dwBufferCount,
  _Out_  LPDWORD lpNumberOfBytesSent,
  _In_   DWORD dwFlags,
  _In_   LPWSAOVERLAPPED lpOverlapped,
  _In_   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

例子,来自MSDN
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT 27015

int main() {

    //----------------------
    // Declare and initialize variables.
    int iResult;
    WSADATA wsaData;

    SOCKET ConnectSocket = INVALID_SOCKET;
    struct sockaddr_in clientService; 

    int recvbuflen = DEFAULT_BUFLEN;
    char *sendbuf = "Client: sending data test";
    char recvbuf[DEFAULT_BUFLEN] = "";

    //----------------------
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    //----------------------
    // Create a SOCKET for connecting to server
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
    clientService.sin_port = htons( DEFAULT_PORT );

    //----------------------
    // Connect to server.
    iResult = connect( ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) );
    if (iResult == SOCKET_ERROR) {
        wprintf(L"connect failed with error: %d\n", WSAGetLastError() );
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
  }

    //----------------------
    // Send an initial buffer
    iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
    if (iResult == SOCKET_ERROR) {
        wprintf(L"send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    printf("Bytes Sent: %d\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if ( iResult > 0 )
            wprintf(L"Bytes received: %d\n", iResult);
        else if ( iResult == 0 )
            wprintf(L"Connection closed\n");
        else
            wprintf(L"recv failed with error: %d\n", WSAGetLastError());

    } while( iResult > 0 );


    // close the socket
    iResult = closesocket(ConnectSocket);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"close failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    WSACleanup();
    return 0;
}


















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值