Winsock网络编程笔记 : 基于TCP的 Client

每次写网络程序都必须编写代码载入和释放winsock库,为了以后方便使用,我们将封装一个CInitSock类来管理Winsock库: 

// initsock.h文件
 
#include <winsock2.h>
#pragma comment(lib, "WS2_32")	// 链接到WS2_32.lib
 
class CInitSock		
{
public:
	CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
	{
		// 初始化WS2_32.dll
		WSADATA wsaData;
		WORD sockVersion = MAKEWORD(minorVer, majorVer);
		if(::WSAStartup(sockVersion, &wsaData) != 0)
		{
			exit(0);
		}
	}
	~CInitSock()
	{	
		::WSACleanup();	
	}
};
#include"../common/initsock.h"
#include<iostream>
using namespace std;

CInitSock initSock;

int main()
{
	SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建socket套接字s
	if (s == INVALID_SOCKET)
	{
		cout << "创建socket失败!" << endl;
		return 0;
	}
	sockaddr_in serverAddress;                       //创建服务器地址
	serverAddress.sin_family = AF_INET;              //sin_family指代协议族,在socket编程中只能是AF_INET
	serverAddress.sin_port = htons(4567);             //绑定服务器端口号
	serverAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //绑定服务器IP地址,sin_addr用的是in_addr的结构储存IP

	if (::connect(s, (sockaddr*)&serverAddress, sizeof(serverAddress)) == -1)//初始化serverAddress并判断connect函数返回值
	{
		cout << "客户端连接失败!" << endl;
		return 0;
	}
	char receivedData[256];
	int index = ::recv(s, receivedData, 256, 0);
	if (index > 0)
	{
		receivedData[index] = '';
		cout << "接收到来自服务端的数据为: " << receivedData << endl;
	}
	::closesocket(s);
	return 0;
}

 

部分函数详解:

struct sockaddr_in

struct sockaddr_in
{
    short sin_family;
/*Address family一般来说AF_INET(地址族)PF_INET(协议族),在socket编程中只能是AF_INET*/
 
    unsigned short sin_port;
/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字),
    sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的
    端口号已经被系统使用或保留。*/
 
    struct in_addr sin_addr;
/*sin_addr存储IP地址,使用in_addr这个数据结构*/
 
    unsigned char sin_zero[8];
/*Same size as struct sockaddr没有实际意义,只是为了跟SOCKADDR结构在内存中对齐*/
};


sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockaddr的结构体,
并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,然后用memset函数初始化就可以了。

memset((char*)&mysock,0,sizeof(mysock));//初始化

sockaddr_in mysock;
memset((char*)&mysock,0,sizeof(mysock));
mysock.sin_family=AF_INET;
mysock.sin_port=htons(1234);//1234是端口号
mysock.sin_addr.s_addr=inet_addr("192.168.0.1");

 inet_addr()

in_addr_t  inet_addr(const char* strptr);

/*inet_addr方法可以转化字符串,主要用来将一个十进制的数转化为二进制的数,用途多于ipv4的IP转化。*/
返回:若字符串有效则将字符串转换为32位二进制网络字节序的IPV4地址,否则为INADDR_NONE

struct in_addr{
    in_addr_t   s_addr;
}

所处头文件: #include <arpa/inet.h>

例子:
daddr.s_addr = inet_addr("192.168.1.60");

 connect

int connect(
  __in          SOCKET s,
  __in          const struct sockaddr* name,
  __in          int namelen
);

SOCKET:标识一个套接字。
serv_addr:套接字s想要连接的主机地址和端口号。
namelen:name缓冲区的长度。

recv

int recv(
  __in          SOCKET s,
  __out         char* buf,
  __in          int len,
  __in          int flags
);

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0。
原文地址:https://www.cnblogs.com/Romantic-Chopin/p/12451013.html