标签:
接下来说一下如何用WinSock创建基于TCP/IP模型的客户端和服务器。
TCP可以提供两个计算机间可靠无误的数据传输,应用程序使用TCP通信时,会在两台计算机之间建立一个虚拟连接,连接之后计算机之间变可以以双向字节流进行数据交换。
下面说下简单的发送数据的客户端实现.
创建客户机的连接比较简单:
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC; //可以是IPv4或IPv6地址
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
调用getaddrinfo函数确定服务器ip地址(由命令行参数传递)和端口
#define DEFAULT_PORT "27015"
// Resolve the server address and port
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
socket原型:
socket(
IN int af, //协议的地址族,使用IPv4来描述Winsock,设置为AF_INET
IN int type, //套接字类型,TCP/IP设置为SOCK_STREAM,UDP/IP设置为SOCK_DGRAM
IN int protocol //用于给定地址族和类型具有多重入口的传送限定,TCP设置为IPPROTO_TCP,UDP设置为IPPROTO_UDP
);
调用socket创建嵌套字,错误检测
SOCKET ConnectSocket = INVALID_SOCKET;
ptr=result;
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
//释放资源
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
客户端程序在创建套节字之后,要使用 connect函数请求与服务器连接,connect原型:
int WSAAPI connect(
IN SOCKET s, //要建立连接的socket
IN const struct sockaddr FAR * name, //指向保存要建立连接信息的地址结构
IN int namelen //参数2指向地址结构的大小
);
收发数据才是网络编程的主题,在已经建立连接的套接字上发生数据可以使用send或WSASend(WinSock2中),接受可用recv和WSARecv。收发数据都是用char类型(面向字节的数据)。
#define DEFAULT_BUFLEN 512
int recvbuflen = DEFAULT_BUFLEN;
char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
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);
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
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);
所有收发数据返回的错误代码都是SOCKET_ERROR,一旦有错误返回,系统就会调用WSAGetLastError()获取详细错误信息。
常见错误:
WSAECONNABORTED和WSAECONNRESET:连接正在关闭(超时或者由于通信放正在关闭连接)
WSAEWOULDBLOCK:套接字处于非阻塞模式或异步状态。
使用send和recv函数原型。
int send(
SOCKET s, // 套节字句柄
const char FAR* buf,// 要发送的数据的缓冲区的地址
int len, // 缓冲区的长度
int flags // 指定了的调用方式,通常设为0
);
int recv(
SOCKET s,
char FAR* buf,
int len,
int flags
);
send函数在一个连接的套节字上发送缓冲区内的数据,返回发送数据的实际字节数,recv函数从对方接收数据,并存储它到指定的缓冲区。flag参数在这两个函数中通常设为0。
在阻塞模式下,send将会阻塞线程的执行直到所有的数据发送完毕(或者一个错误的发生),而recv函数将返回尽可能多的当前信息,一直到缓冲区指定的大小。
函数执行失败返回INVALID_SOCKET(-1),应该调用closesocket函数将它关闭。如果没有错误发生,函数返回0,否则返回SOCKET_ERROR。函数用法如下:
int closesocket(
__in SOCKET s // 函数唯一的参数就是要关闭的套节字的句柄
);
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
closesocket(ConnectSocket);
WSACleanup();
附完整的客户端代码:
#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <WinSock2.h> #include <WS2tcpip.h> #include <IPHlpApi.h> #include <stdio.h> #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Mswsock.lib") #pragma comment(lib, "Advapi32.lib") #define DEFAULT_PORT "27015" #define DeFAULT_BUFLEN 512 int main(int argc, char* argv[]) { WSADATA wsaData; int iResult; struct addrinfo *result = NULL, *ptr = NULL, hints; char *sendbuf = "this is a test"; char recvbuf[DeFAULT_BUFLEN]; int recvbuflen = DeFAULT_BUFLEN; SOCKET ConnectSocket = INVALID_SOCKET; if (argc != 2) { printf("usage: %s server-name\n", argv[0]); } // 初始化winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // 确定服务器地址和端口 iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result); if (iResult != 0) { printf("getaddrinfo failed: %d\n", iResult); WSACleanup(); return 1; } // 尝试连接到服务器地址,直到成功 for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { // 创建套接字连接到服务器 ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // 连接服务器 iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); if (iResult == SOCKET_ERROR) { closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; continue; } break; } // 释放资源 freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) { printf("Unable to connect to server!\n"); WSACleanup(); return 1; } // 发送sendbuf内容 iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf) + 1, 0); if (iResult == SOCKET_ERROR) { printf("send failed:%d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } printf("Byte Send:%ld\n", iResult); // 数据发送完成之后断开连接 iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown faild: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } // 收到回应之后关闭连接 do { iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); if (iResult > 0) printf("Byte received: %d\n", iResult); else if (iResult == 0) printf("connection closed\n"); else printf("recv failed: %d\n", WSACleanup()); } while (iResult > 0); // 清除套接字 closesocket(ConnectSocket); WSACleanup(); return 0; }
本文链接: http://www.bugcoding.com/entry/10
标签:
原文地址:http://www.cnblogs.com/NGNL/p/4534601.html