首先吐槽以下微软的socket的两个最重要的API:send函数和recv函数,本菜鸟感觉这两个函数是用来给程序员自己开发的函数,用起来跟翔一样的体验。
int recv(
_In_ SOCKET s,
_Out_ char *buf,
_In_ int len,
_In_ int flags
);
int send(
_In_ SOCKET s,
_In_ const char *buf,
_In_ int len,
_In_ int flags
);
这是这两个函数的原型,参数含义我就不解释了,这里面常见的两种错误:
1、发送长度问题。这是个老生常谈的问题,在send一串字符的时候应该首先发送这串字符的长度n,然后才能在recv的时候recv(HANDLE , recvstring , n , 0);
2、还是发送长度的问题。在现实程序中发现一个问题,当发送长度过大时候,经常一次recv不够完全,需要多次recv才能接收完全,经常发现send 10000个字符,recv返回的接收字符串长度小于10000。这时候就应该这样写
int cSocket::SendBits(char * s , int bytes)
{
int sendbytes = 0;
while(sendbytes < bytes) //实际发送长度小于应该发送长度</span>
{
sendbytes+= send( hSocket , (char *)(s+sendbytes) , bytes-sendbytes , 0 );
}
return sendbytes;
}
int cSocket::RecvBits(char *& s , int bytes)
{
int recvbytes = 0;
while(recvbytes < bytes) //实际接收长度小于应该接收长度
{
recvbytes += recv( hSocket , s+recvbytes , bytes-recvbytes , 0 );
}
return recvbytes;
}
再来对比以下MSDN上面的例子
#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 __cdecl main() {
//----------------------
// Declare and initialize variables.
WSADATA wsaData;
int iResult;
SOCKET ConnectSocket = INVALID_SOCKET;
struct sockaddr_in clientService;
char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
//----------------------
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for connecting to server
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %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( 27015 );
//----------------------
// Connect to server.
iResult = connect( ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) );
if ( iResult == SOCKET_ERROR) {
closesocket (ConnectSocket);
printf("Unable to connect to server: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Send an initial buffer
iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %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 )
printf("Bytes received: %d\n", iResult);
else if ( iResult == 0 )
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while( iResult > 0 );
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
MSDN在处理recvbuf的时候任然没有判断iResult和recvbuflen之间的关系。为整个程序埋下了隐患。(其实在MSDN remark中有简单说明)真是坑啊。