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] = '\0';
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。