码迷,mamicode.com
首页 > Windows程序 > 详细

WinSock网络编程基础(2)客户端

时间:2015-05-28 00:11:46      阅读:293      评论:0      收藏:0      [点我收藏+]

标签:

接下来说一下如何用WinSock创建基于TCP/IP模型的客户端和服务器。

TCP可以提供两个计算机间可靠无误的数据传输,应用程序使用TCP通信时,会在两台计算机之间建立一个虚拟连接,连接之后计算机之间变可以以双向字节流进行数据交换。

下面说下简单的发送数据的客户端实现.

创建客户机的连接比较简单:

 

1.创建一个套接字,定义addrinfo对象并初始化这些值(该对象包含一个sockaddr结构)

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;
}

 

2.连接到的服务器名。(在TCP/IP中就是监听服务器的IP地址和端口号)

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指向地址结构的大小 

        ); 

 

3.发送和接收数据。

 

收发数据才是网络编程的主题,在已经建立连接的套接字上发生数据可以使用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 // 函数唯一的参数就是要关闭的套节字的句柄

);

 

4.断开连接,关闭套接字(当客户机已经发出数据,可以使用shutdown函数和SD_SEND宏关闭发送套接字,客户机仍然允许接受来自服务器套接字上的数据)

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

WinSock网络编程基础(2)客户端

标签:

原文地址:http://www.cnblogs.com/NGNL/p/4534601.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!