C++ socket 简单通讯

一、socket通讯开发步骤
1、创建socket
1.1、指定服务端相关信息系
指定socket版本号 如: winsock_ver = MAKEWORD(2, 2);
addr_len = sizeof(SOCKADDR_IN);
//指定ip地址族
addr_svr.sin_family = AF_INET;
//转换网络编码,指定侦听本地端口
addr_svr.sin_port = ::htons(SERVER_PORT);
//设置接收任意地址的连接
addr_svr.sin_addr.S_un.S_addr = ADDR_ANY;
//设置某一指定内存内容为0;
memset(buf_ip, 0, IP_BUF_SIZE)将buf_ip全部指定为0, IP_BUF_SIZE:IP地址长度
1.2、启动socket

//WSAStartup,即WSA(Windows Sockets Asynchronous,Windows异步套接字)的启动命令。0表示成功
	ret_val = ::WSAStartup(winsock_ver, &wsa_data);
	if (ret_val != 0)
	{
		cerr << "WSA failed to start up!Error code: " << ::WSAGetLastError() << "\n";
		system("pause");
		exit(1);
	}
	cout << "WSA started up successfully...\n";
1.3、创建服务端socket
//创建socket  socket如果调用成功就返回新创建的套接字的描述符
	sock_svr = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock_svr == INVALID_SOCKET)
	{
		cerr << "Failed to create server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket created successfully...\n";

2、绑定
//bind 绑定要通信的程序,成为服务器,如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
//sock_svr就是第二步成功买回的,不,是申请到的接口
//(SOCKADDR*)&addr_svr 是指进程具体地址,就像具体电话号码一样,他的类型是一个结构体
//addr_len就是位置的长度

ret_val = ::bind(sock_svr, (SOCKADDR*)&addr_svr, addr_len);
	if (ret_val != 0)
	{
		cerr << "Failed to bind server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket bound successfully...\n";

3、监听端口

ret_val = ::listen(sock_svr, SOMAXCONN);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Server socket failed to listen!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket started to listen...\n";
	//
	cout << "Server started successfully..." << endl;

4、等待客户端,

while (true)
	{
		//接受客户端的信息
		sock_clt = ::accept(sock_svr, (SOCKADDR*)&addr_clt, &addr_len);
		cout << "ssss " << sock_clt << endl;
		if (sock_clt == INVALID_SOCKET)
		{
			cout << "ssss " << sock_clt <<endl;
			cerr << "Failed to accept client!Error code: " << ::WSAGetLastError() << "\n";
			::WSACleanup();
			system("pause");
			exit(1);
		}

5、接收客户端发送的信息、发送信息给客户端

char buf_msg[MSG_BUF_SIZE];  //  接收信息
	int ret_val = 0;   //返回读入字节数
	int snd_result = 0;  //返回发送信息字节数

	do
	{
		memset(buf_msg, 0, MSG_BUF_SIZE);
		//接收数据  若无错误,返回读入字节数
		ret_val = ::recv(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
		if (ret_val > 0)
		{
			//如果接送的信息为“exit”,则关闭连接
			if (strcmp(buf_msg, "exit") == 0)
			{
				cout << "Client requests to close the connection..." << endl;
				break;
			}
			//打印接收信息
			cout << "Message received: " << buf_msg << endl;

			//发送消息给客户端
			char send_to_client[SEND_BUF_SIZE];  //发送给客户端的信息
			cout << "enter the message that you want to send client: " << flush;
			cin.getline(send_to_client, SEND_BUF_SIZE);//控制台输入需要发送信息
			cout << "enter the message " << send_to_client << endl;
			snd_result = ::send(sock_clt, send_to_client, static_cast<int>(strlen(send_to_client)), 0);

			//snd_result = ::send(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
			//如果发送失败,即snd_result=-1
			if (snd_result == SOCKET_ERROR)
			{
				cerr << "Failed to send message to client!Error code: " << ::GetLastError() << "\n";
				::closesocket(sock_clt);
				system("pause");
				return 1;
			}
		}//接受信息失败ret_val==0
		else if (ret_val == 0)
		{
			cout << "connection closed..." << endl;
		}
		else
		{
			cerr << "Failed to receive message from client!Error code: " << ::GetLastError() << "\n";
			::closesocket(sock_clt);
			system("pause");
			return 1;
		}
	} while (ret_val > 0);
	//
	ret_val = ::shutdown(sock_clt, SD_SEND);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Failed to shutdown the client socket!Error code: " << ::GetLastError() << "\n";
		::closesocket(sock_clt);
		system("pause");
		return 1;
	}

6、创建客户端socket
6.1、同样先创建socket

WSADATA wsa_data; //WSADATA变量,包含windows socket执行的信息
	int i_result = 0; //接收返回值
	SOCKET sock_client = INVALID_SOCKET;
	addrinfo *result = nullptr, hints;
	//初始化winsock动态库(ws2_32.dll),MAKEWORD(2, 2)用于请求使用winsock2.2版本
	i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
	if (i_result != 0) {
		cerr << "WSAStartup() function failed: " << i_result << "\n";
		system("pause");
		return 1;
	}
	SecureZeroMemory(&hints, sizeof(hints));//其作用是用0来填充一块内存区域
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	//getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,
	//返回的是一个sockaddr结构的链表而不是一个地址清单。
	//这些sockaddr结构随后可由套接口函数直接使用
	i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
	if (i_result != 0) {
		cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	//创建套接字
	sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (sock_client == INVALID_SOCKET) {
		cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}

6.2、连接服务器

//连接服务器
	i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
	if (i_result == SOCKET_ERROR) {
		cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	cout << "connect server successfully..." << endl;
	//释放由getaddrinfo返回的存储空间,包括addrinfo结构、ai_addr结构和ai_canonname字符串
	freeaddrinfo(result);

6.3、发送信息给服务端

//发送信息给服务端
	char send_buf[SEND_BUF_SIZE];
	int recv_result = 0; //接收服务器返回的数据的返回值
	//SecureZeroMemory(send_buf, SEND_BUF_SIZE);
	do {
		cout << "enter the message that you want to send: " << flush;
		cin.getline(send_buf, SEND_BUF_SIZE);
		i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
		if (i_result == SOCKET_ERROR) {
			cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
		cout << "Bytes sent: " << i_result << endl;
		
		//接收服务器返回的数据
		char rec_from_server[SEND_BUF_SIZE];
		recv_result = recv(sock_client, rec_from_server, SEND_BUF_SIZE, 0);
		//recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
		if (recv_result > 0) {
			cout << "feedback from server: " << rec_from_server << endl;
			//cout << "feedback from server: " << send_buf << endl;
		}
		else if (recv_result == 0) {
			cout << "connection closed..." << endl;
		}
		else {
			cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
	} while (recv_result > 0);

7、关闭socket

i_result = shutdown(sock_client, SD_SEND);
	if (i_result == SOCKET_ERROR) {
		cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
		closesocket(sock_client);
		WSACleanup();
		system("pause");
		return 1;
	}
	closesocket(sock_client);
	WSACleanup();
	cout << "socket closed..." << endl;

服务端代码:
1、socketserver.h

#pragma once
#ifndef SOCKETSERVER_H
#define SOCKETSERVER_H

#include <Winsock2.h>
#include <windows.h>

#pragma comment (lib, "ws2_32.lib")

#define IP_BUF_SIZE 129

class Server
{
public:
	Server();
	~Server();
	Server(const Server &) = delete;
	Server & operator=(const Server &) = delete;
	void WaitForClient();
private:
	WORD winsock_ver;  //socket版本
	WSADATA wsa_data;  //存储被WSAStartup函数调用后返回的Windows Sockets数据
	SOCKET sock_svr;   //服务端socket
	SOCKET sock_clt;   //客户端socket
	HANDLE h_thread;   //创建的线程
	SOCKADDR_IN addr_svr;  //存放了服务端地址族、端口、ip地址,用来处理通信地址
	SOCKADDR_IN addr_clt;  //存放了客户端地址族、端口、ip地址,用来处理通信地址
	int ret_val;
	int addr_len;
	char buf_ip[IP_BUF_SIZE];
};

#endif


2、socketserver.cpp

#include "pch.h"
#include "socketserver.h"
#include <iostream>
#include <WS2tcpip.h>


using namespace std;

#define SERVER_PORT 5000     //端口
#define MSG_BUF_SIZE 1024    //接送信息最大长度

const int SEND_BUF_SIZE = 1024;  //发送信息最大长度

Server::Server()
{
	cout << "Initializing server...\n";
	//
	winsock_ver = MAKEWORD(2, 2);    //指定socket版本2.2
	addr_len = sizeof(SOCKADDR_IN);
	addr_svr.sin_family = AF_INET; //ip地址族
	addr_svr.sin_port = ::htons(SERVER_PORT);  //转换网络编码,侦听本地5000端口
	addr_svr.sin_addr.S_un.S_addr = ADDR_ANY;  //接收任意地址的连接

	//memset某一块内存中的内容全部设置为指定的值, 
	//这个函数通常为新申请的内存做初始化工作。
	memset(buf_ip, 0, IP_BUF_SIZE);//将buf_ip全部指定为0

	//WSAStartup,即WSA(Windows Sockets Asynchronous,Windows异步套接字)的启动命令。0表示成功
	ret_val = ::WSAStartup(winsock_ver, &wsa_data);
	if (ret_val != 0)
	{
		cerr << "WSA failed to start up!Error code: " << ::WSAGetLastError() << "\n";
		system("pause");
		exit(1);
	}
	cout << "WSA started up successfully...\n";
	//创建socket  socket如果调用成功就返回新创建的套接字的描述符
	sock_svr = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock_svr == INVALID_SOCKET)
	{
		cerr << "Failed to create server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket created successfully...\n";

	//bind  绑定要通信的程序,成为服务器,如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
	//sock_svr就是第二步成功买回的,不,是申请到的接口
	//(SOCKADDR*)&addr_svr 是指进程具体地址,就像具体电话号码一样,他的类型是一个结构体
	//addr_len就是位置的长度
	ret_val = ::bind(sock_svr, (SOCKADDR*)&addr_svr, addr_len);
	if (ret_val != 0)
	{
		cerr << "Failed to bind server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket bound successfully...\n";
	//监听端口
	ret_val = ::listen(sock_svr, SOMAXCONN);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Server socket failed to listen!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket started to listen...\n";
	//
	cout << "Server started successfully..." << endl;
	cout << "ret_val..." << ret_val << endl;
}

Server::~Server()
{
	::closesocket(sock_svr);
	::closesocket(sock_clt);
	::WSACleanup();
	cout << "Socket closed..." << endl;
}

DWORD WINAPI CreateClientThread(LPVOID lpParameter);

//等待客户端
void Server::WaitForClient()
{
	while (true)
	{
		//接受客户端的信息
		sock_clt = ::accept(sock_svr, (SOCKADDR*)&addr_clt, &addr_len);
		cout << "ssss " << sock_clt << endl;
		if (sock_clt == INVALID_SOCKET)
		{
			cout << "ssss " << sock_clt <<endl;
			cerr << "Failed to accept client!Error code: " << ::WSAGetLastError() << "\n";
			::WSACleanup();
			system("pause");
			exit(1);
		}
		//::InetNtop(addr_clt.sin_family, &addr_clt, buf_ip, IP_BUF_SIZE);
		//cout << "A new client connected...IP address: " << buf_ip << ", port number: " << ::ntohs(addr_clt.sin_port) << endl;


		//:CreateThread:新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,
		//线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象
		h_thread = ::CreateThread(nullptr, 0, CreateClientThread, (LPVOID)sock_clt, 0, nullptr);
		if (h_thread == NULL)
		{
			cerr << "Failed to create a new thread!Error code: " << ::WSAGetLastError() << "\n";
			::WSACleanup();  //终止Winsock 2 DLL (Ws2_32.dll) 的使用
			system("pause");
			exit(1);
		}
		//关闭线程关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。
		//在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,
		//引用计数减1,当变为0时,系统删除内核对象。
		::CloseHandle(h_thread);
	}
}
//创建客户端线程
DWORD WINAPI CreateClientThread(LPVOID lpParameter)
{
	SOCKET sock_clt = (SOCKET)lpParameter;  //客户端
	char buf_msg[MSG_BUF_SIZE];  //  接收信息
	int ret_val = 0;   //返回读入字节数
	int snd_result = 0;  //返回发送信息字节数

	do
	{
		memset(buf_msg, 0, MSG_BUF_SIZE);
		//接收数据  若无错误,返回读入字节数
		ret_val = ::recv(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
		if (ret_val > 0)
		{
			//如果接送的信息为“exit”,则关闭连接
			if (strcmp(buf_msg, "exit") == 0)
			{
				cout << "Client requests to close the connection..." << endl;
				break;
			}
			//打印接收信息
			cout << "Message received: " << buf_msg << endl;

			//发送消息给客户端
			char send_to_client[SEND_BUF_SIZE];  //发送给客户端的信息
			cout << "enter the message that you want to send client: " << flush;
			cin.getline(send_to_client, SEND_BUF_SIZE);//控制台输入需要发送信息
			cout << "enter the message " << send_to_client << endl;
			snd_result = ::send(sock_clt, send_to_client, static_cast<int>(strlen(send_to_client)), 0);

			//snd_result = ::send(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
			//如果发送失败,即snd_result=-1
			if (snd_result == SOCKET_ERROR)
			{
				cerr << "Failed to send message to client!Error code: " << ::GetLastError() << "\n";
				::closesocket(sock_clt);
				system("pause");
				return 1;
			}
		}//接受信息失败ret_val==0
		else if (ret_val == 0)
		{
			cout << "connection closed..." << endl;
		}
		else
		{
			cerr << "Failed to receive message from client!Error code: " << ::GetLastError() << "\n";
			::closesocket(sock_clt);
			system("pause");
			return 1;
		}
	} while (ret_val > 0);
	//
	ret_val = ::shutdown(sock_clt, SD_SEND);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Failed to shutdown the client socket!Error code: " << ::GetLastError() << "\n";
		::closesocket(sock_clt);
		system("pause");
		return 1;
	}
	return 0;
}

3、启动服务端代码

#include "pch.h"
#include "socketserver.h"
int main()
{
	Server svr;
	svr.WaitForClient();
	system("pause");
	return 0;
}

4、客户端代码

#include <iostream>
#include <WinSock2.h>
#include <Ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

using std::cin;
using std::cerr;
using std::cout;
using std::endl;
using std::flush;

const char DEFAULT_PORT[] = "5000";
const int SEND_BUF_SIZE = 1024;

//客户端
int main() {
	WSADATA wsa_data; //WSADATA变量,包含windows socket执行的信息
	int i_result = 0; //接收返回值
	SOCKET sock_client = INVALID_SOCKET;
	addrinfo *result = nullptr, hints;
	//初始化winsock动态库(ws2_32.dll),MAKEWORD(2, 2)用于请求使用winsock2.2版本
	i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
	if (i_result != 0) {
		cerr << "WSAStartup() function failed: " << i_result << "\n";
		system("pause");
		return 1;
	}
	SecureZeroMemory(&hints, sizeof(hints));//其作用是用0来填充一块内存区域
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	//getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,
	//返回的是一个sockaddr结构的链表而不是一个地址清单。
	//这些sockaddr结构随后可由套接口函数直接使用
	i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
	if (i_result != 0) {
		cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	//创建套接字
	sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (sock_client == INVALID_SOCKET) {
		cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	//连接服务器
	i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
	if (i_result == SOCKET_ERROR) {
		cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	cout << "connect server successfully..." << endl;
	//释放由getaddrinfo返回的存储空间,包括addrinfo结构、ai_addr结构和ai_canonname字符串
	freeaddrinfo(result);

	//发送信息给服务端
	char send_buf[SEND_BUF_SIZE];
	int recv_result = 0; //接收服务器返回的数据的返回值
	//SecureZeroMemory(send_buf, SEND_BUF_SIZE);
	do {
		cout << "enter the message that you want to send: " << flush;
		cin.getline(send_buf, SEND_BUF_SIZE);
		i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
		if (i_result == SOCKET_ERROR) {
			cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
		cout << "Bytes sent: " << i_result << endl;
		
		//接收服务器返回的数据
		char rec_from_server[SEND_BUF_SIZE];
		recv_result = recv(sock_client, rec_from_server, SEND_BUF_SIZE, 0);
		//recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
		if (recv_result > 0) {
			cout << "feedback from server: " << rec_from_server << endl;
			//cout << "feedback from server: " << send_buf << endl;
		}
		else if (recv_result == 0) {
			cout << "connection closed..." << endl;
		}
		else {
			cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
	} while (recv_result > 0);
	//
	i_result = shutdown(sock_client, SD_SEND);
	if (i_result == SOCKET_ERROR) {
		cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
		closesocket(sock_client);
		WSACleanup();
		system("pause");
		return 1;
	}
	closesocket(sock_client);
	WSACleanup();
	cout << "socket closed..." << endl;
	system("pause");
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

print_out

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值