C++实现简单通信(VS 2017+ win 10)

一.前言

学计算机迟早会接触到通信。
而简单可用的代码来入门很重要。
但网上以及书本上给的的版本可能都不太易用。
本文主要涉及简单的客户端与服务端可用编码,旨在于注释中讲道理。(直接粘代码到VS里看吧)

二.引入

如果大家之前一点都没接触到网络通信的话,那么这里给大家介绍一下Socket:一个用于通信的接口
c++简单的通信用它行,不用考虑什么其他的库优劣性,可以说是很省心了。
想要了解更多的话,本文不提供。你去搜《计算机网络》,你值得拥有。

三.开门见山铺代码

一点点引导:
通信肯定要大于等于两个个体才能通信。
在这里我们简单的分成了:服务器端 客户端
以下代码使用TCP的方法实现的,关于TCP其他文章有很多讲述,这里就不一一论述了。

服务端代码:

/*服务端
**SocketService.cpp
**2018/12/28
**Author:零医一零
**注:由于注重解释学习,所以请忽略该代码的格式
*/

#include<WINSOCK2.h>//windows socket的头文件 系统自带
#include<iostream>//这个都不知道我也没办法
using namespace std;//这句话我也很无奈啊

#pragma comment(lib,"ws2_32.lib")//用网络API函数的时候,就要用这条语句加载ws2_32.lib库(或者你自己去动态载入ws2_32.dll)

int main()
{
	WSADATA wsaData;//用来存储被 WSAStartup 函数调用后返回的数据
	WSAStartup(0x0202, &wsaData);
	/*
	必要性:WSAStartup必须是Windows Sockets中第一个使用的函数,成功调用后才能进一步使用Windows Sockets API函数
	作用:指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节
	用法:前参是一个WORD(双字节)型数值,指定了应用程序需要使用的Winsock规范的最高版本。
		后参是WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。
	*/

	//1.套接字(SOCKET神奇的翻译)的创建和关闭
	SOCKET listenSocket; //定义套接字
	listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//创建套接字
	/*
	作用:创建套接字时返回小整数作为描述符来标识它
	用法:AF表示ADDRESS FAMILY 地址族,SOCK_STREAM表示能保证数据正确传送到对方的SOCKET,IPPROTO_TCP表示TCP协议
	补充:当不使用socket创建的套接字时,应该调用closesocket函数将它关闭(int closesocket(SOCKET s);  )
	*/

	//2.绑定套接字到指定的IP地址和端口号
	SOCKADDR_IN local;
	//用于建立listenSocket的本地关联sockaddr_in结构
	local.sin_family = AF_INET;
	//与socket函数中的af参数的含义相同,见上
	local.sin_port = htons(1234);
	//如果端口号等于0,执行时系统会自动分配唯一的端口号,其值在1024~5000之间。
	local.sin_addr.s_addr = htonl(INADDR_ANY);//如果地址等于INADDR_ANY,会自动使用当前主机配置的所有IP地址;
	/*也可以是local.S_un.S_addr.s_addr = htonl(INADDR_ANY);(因为winsock2.h中定义了#define s_addr  S_un.S_addr)
	sin_port字段和sin_addr字段分别指定套接字需要绑定的端口号和IP地址。放入这两个字段的数据的字节顺序必须是网络字节顺序
	(因为网络字节顺序和Intel CPU的字节顺序刚好相反,所以必须首先使用htons函数进行转换)*/
	bind(listenSocket, (struct sockaddr *) &local, sizeof(local));
	/*
	作用:bind函数用在没有建立连接的套接字上,它的作用是绑定面向连接的或者无连接的套接字。
		套接字被socket函数创建以后,存在于指定的地址家族里,但它是未命名的。
		bind函数通过安排一个本地名称到未命名的socket而建立此socket的本地关联。
	用法:1.SOCKET 2.SOCKADDR_IN	 3.SOCKADDR_IN的长度
	*/

	//3.设置套接字进入监听状态	
	listen(listenSocket, 1);
	cout << "开启监听:\n";
	/*
	作用:设置套接字进入监听状态。
	用法:套接字,监听队列中允许保持的尚未处理的最大连接数量
	特性:函数执行成功后,套接字s进入了被动模式,到来的连接会被通知要排队等候接受处理。
		在同一时间处理多个连接请求的服务器通常使用listen函数
		如果一个连接请求到达,并且排队已满,客户端将接收到WSAECONNREFUSED错误。
	*/

	//4.接受连接请求
	SOCKET clientSocket;//忘记了就看上面
	SOCKADDR_IN client;//忘记了就看上面
	int addrSize = sizeof(SOCKADDR_IN);
	clientSocket = accept(listenSocket, (struct sockaddr *) &client, &addrSize);
	/*
	作用:取出未处理连接中的第一个连接,然后为这个连接创建新的套接字,返回它
	特性:程序默认工作在阻塞模式下:如果没有未处理的连接存在,就会等待下去,直到有新连接发生来进行处理
	用法:addrlen参数用于指定addr所指空间的大小,也用于返回地址的实际长度。如果是NULL,则没有关于远程地址的信息返回
	*/
	cout << "Accepted client:%s:%d\n"//反馈连接信息
		<< inet_ntoa(client.sin_addr)//inet_ntoa将一个十进制网络字节序转换为点分十进制IP格式的字符串。
		<< ntohs(client.sin_port);  //ntohs将一个16位数由网络字节顺序转换为主机字节顺序

//5.与客户端通信
	int const CLIENT_MSG_SIZE = 128;//接收缓冲区长度
	char cacheMSG[CLIENT_MSG_SIZE]; //接收缓冲区的基地址
	while (TRUE) {
		int size = recv(clientSocket, cacheMSG, CLIENT_MSG_SIZE, 0);
		/*
		作用:recv()从接收缓冲区拷贝数据。成功时,返回拷贝的字节数,失败返回-1。
		用法:前三个请找上述的参数定义。最后一个参数为0的意义:默认是普通接受数据模式
		特性:阻塞模式下,recv将会阻塞到缓冲区里至少有一个字节,没有数据时处于休眠状态。
			若非阻塞,则立即返回,有数据则返回拷贝的数据大小,否则返回错误-1。
		*/
		cacheMSG[size] = '\0'; //你懂得   
		cout << "Received:" << cacheMSG<< endl;
	}

//6.结束收尾
	closesocket(listenSocket);
	closesocket(clientSocket);
	WSACleanup();
	return 0;
}

服务端部分的实现过程可简述为:
1.创建套接字并初始化
2.绑定套接字到指定地址
3.开启监听
4.接受连接请求
5.获取信息

客户端代码

/*客户端
**SocketClient.cpp
**2018/12/28
**Author:零医一零
**注:由于注重解释学习,所以请忽略该代码的格式
*/
#include <WINSOCK2.H>   
#include<iostream>
using namespace std;
                    
#pragma comment(lib, "ws2_32.lib")      
                   
int main()      
{      
    WSADATA wsaData; 	
    WSAStartup(0x0202, &wsaData);//申请Windows Sockets API支援
    SOCKET clientSocket;//创建并初始化套接字
    clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    SOCKADDR_IN server;//填充服务器地址信息
    server.sin_family = PF_INET;       
    server.sin_port = htons(1234); 
    server.sin_addr.s_addr = inet_addr("127.0.0.1");    
//过了一遍服务器的代码,上面肯定就看得懂了。不懂,就回头再看看吧。
    
//连接服务器   
    connect(clientSocket, (struct sockaddr *) &server, sizeof(server)); 
    /*
	作用:连接后可以用clientSocket来使用这个连接 
	用法:要记录的套接字,保存了远程地址信息的server,地址大小。
	P.S:跟服务器里的recv()几乎相同。
	*/
	
//发送信息
    while (TRUE) {      
		char Msg[128];//定义发送缓存区
		cout<<"Send:"; cin>>Msg;//输入要发送的字节
    	send(clientSocket, Msg, strlen(Msg), 0); 
    	/*
        作用:snd()从发送缓冲区拷贝数据。
		用法:前三个请找上述的参数定义。最后一个参数为0的意义:默认是普通接受数据模式
		*/
    }      
                   
//结束客户端;      
	closesocket(clientSocket);
    WSACleanup();      
    return 0;      
}  

客户端部分的实现过程可简述为:
1.创建套接字并初始化
2.绑定套接字到指定地址
4.发送连接请求
5.发送信息

四.结语

作者尽最大的能力去解释其中每一行代码的缘由,希望大家看完后,能够很清晰的了解如何实现c++通信程序。
但该代码实现的程序只是建立在本地的通信连接上。
也没有考虑到关闭,依靠while(true)来持续运行程序。
有兴趣的读者可以独自完善,来模拟一些简单通讯软件。

  • 13
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值