[TCP/IP网络编程]使用C++实现简单的TCP通信(windows)

工具:VS2015

学习前先稍微了解下系统提供的动态链接库(DLL),windows API中所有函数都包含在里面,这里不深入讲解,只需知道接下来的socket编程要用到里面的各种函数就行了,一般来说我们能学会调用就已经足够了,对应的头文件为 winsock2.h。

使用DLL前必须把DLL加载到当前程序,加载方式分为动态加载和静态加载两种,这里我使用静态加载做示范,需要用到 #pragma 命令,可以在编译时加载,形式为: #pragma comment(lib,"ws2_32.lib"),这是告诉编译器要加载一个库,而这个库的名字叫做“ws2_32.lib”。那么我们加载完了就可以直接使用了吗?答案时否定了,我们还需要对其进行初始化。初始化需要用到一个函数

int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData)。wVersionRequested 为 WinSock 规范的版本号,lpWSAData 为指向WSADATA结构体的指针。这里我们使用得版本号为2.2,于是这样写WSAStartup(MAKEWORD(2, 2), &wsadata);函数第一个参数类型为WORD,等价于unsigned short ,所以需要用MAKEWORD()进行转换,接下来我们定义一个WSADATA 指针放在第二位就搞定了。以上的代码总结为以下

#include
#include
#include
using namespace std;
#pragma comment(lib,"ws2_32.lib")

int main()
{
        //初始化DLL
	WSADATA wsadata;
        WSAStartup(MAKEWORD(2, 2), &wsadata);
        return 0;
}

因为我们进行的是TCP通信,所以要用到的传输方式为SOCK_STREAM,表示面向连接的可靠传输,对应得UDP使用的传输方式为SOCK_DGRAM,表示无连接的不可靠传输。先给出一个服务器端的代码,再一一分析。

//服务器端.cpp
#include
#include
#include
using namespace std;
#pragma comment(lib,"ws2_32.lib")

int main()
{
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);

	SOCKET serSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));  //用0填充每一个字节
	sockAddr.sin_family = PF_INET;     //使用IPv4地址
	sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的ip地址
	sockAddr.sin_port = htons(1234);   //具体的端口号

	//绑定套接字
	bind(serSock, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
	listen(serSock, 20);
        
        return 0;
}

先看SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);AF_INET代表使用IPv4地址,对应的IPv6用AF_INET6表示,AF是“Address Family”的缩写,INET是“intenet”的缩写;第二个参数SOCK_STREAM代表传输的方式是以字节流的形式;最后一个参数表示使用TCP协议。整个函数返回的是一个服务器端的套接字,用serSock表示。

接下来几行的功能是设置服务器的相关信息,然后使用bind函数将套接字serSock和设置的各种属性绑定,比较需要注意的是第二个参数,需要将sockaddr_in转化为SOCKADDR,这是因为这两个结构体的字节数相同,相互转化不会导致数据得丢失,其次,sockaddr已经将ip地址和端口号合并在一起,想要对两个进行赋值会相当麻烦,而sockaddr_in则把这两个分开,方便赋值,之后再强转化。最后通过listen函数进行监听,第二个参数表示请求队列的数量,具体多少根据情况设置。

完整的服务器端cpp

#include
#include
#include
using namespace std;
#pragma comment(lib,"ws2_32.lib")
const int BUF_SIZE = 100;

int main()
{
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);

	SOCKET serSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));  //用0填充每一个字节
	sockAddr.sin_family = PF_INET;     //使用IPv4地址
	sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的ip地址
	sockAddr.sin_port = htons(1234);   //具体的端口号

	//绑定套接字
	bind(serSock, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
	listen(serSock, 20);

        //接受客户端请求
	SOCKADDR clientAddr;
	int clientAddr_size = sizeof(clientAddr);
	SOCKET clientSock = accept(serSock, (SOCKADDR*)&clientAddr, &clientAddr_size);

        //向客户端发送消息
	char* str = "hello world!!";
	send(clientSock, str, strlen(str)+sizeof(char),NULL);

        //关闭套接字
	closesocket(clientSock);
	closesocket(serSock);
	WSACleanup();

        system("pause");
	return 0;
}

accept()函数:当套接字处于监听状态,可以通过accept()接受客户端的请求。

send()函数:向客户端传输内容。

客户端跟服务端差不多

#include
#include
#include
#include
using namespace std;
#pragma comment(lib,"ws2_32.lib")
const int BUF_SIZE = 100;

int main()
{
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);

        //需要连接服务端的信息
	sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));
	sockAddr.sin_family = AF_INET;
	sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	sockAddr.sin_port = htons(1234);
	
        //创建套接字并连接
	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	connect(sock, (SOCKADDR*)& sockAddr, sizeof(sockAddr));

	char infoBuff[MAXBYTE] = { 0 };
	recv(sock, infoBuff, MAXBYTE,NULL);
	cout << "从服务器接受到得信息为:" << infoBuff << endl;


	closesocket(sock);
	WSACleanup();

	system("pause");
	return 0;
}

recv()函数,从服务端接受传来得信息并存在infoBuff数组里。

先运行服务端的cpp,此时服务端进入监听状态,再运行客户端cpp,便可在控制台上打印出服务端传来的文字。

你可能感兴趣的:(Linux系统编程+网络编程)