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:要传递的、由协议决定的地址的长度
返回值:
例如:
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 connectint 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; }