网络编程基础流程

105 阅读6分钟

服务端:

1.socket——创建socket对象。

2.bind——绑定本机IP和port(端口)。

3.listen——监听来电,若在监听到来电,则建立起连接。

4.accept——再创建一个socket对象给其收发消息。原因是现实中服务端都是面对多个客户端,那么为了区分各个客户端,则每个客户端都需再分配一个socket对象进行收发消息。

5.read、write——就是收发消息了。

客户端:

1.socket——创建socket对象。

2.connect——根据服务端IP+port(端口),发起连接请求。

3.write、read——建立连接后,就可发收消息了。

图示如下

1540562-20190417002428451-62583604.jpg 终端节点的创建 endpoint通过对方ip和端口号构造,其他节点可以链接这个节点进行通信;

对于客户端,构造方法如下:

#include "endpoint.h"
#include<iostream>
#include<boost/asio.hpp>
using namespace boost;
int client_end_point() {
	std::string raw_ip_address = "127.4.8.1";//对端的地址
	unsigned short port_num = 3333;//对端的端口号
	boost::system::error_code ec;//错误码,出错时用来判断
	//将字符串形式地址转化为address对象,并将可能出现的错误信息存储在ec中
	asio::ip::address ip_address = asio::ip::address::from_string(raw_ip_address, ec);
	if (ec.value() != 0) {
		std::cout
			<< "Failed to parse the IP address.Error code="
			<< ec.value() << ".Message is" << ec.message();
		return ec.value();
	}
	//创建一个TCP端点对象ep,其中指定了要连接的目标主机的IP和端口号
	asio::ip::tcp::endpoint ep(ip_address, port_num);
	return 0;
}

服务端构造如下

int server_end_point() {
	unsigned short port_num = 3333;
	//服务器端只需要绑定本地地址即可
	asio::ip::address ip_address = asio::ip::address_v6::any();

	asio::ip::tcp::endpoint ep(ip_address, port_num);
}

创建socket

  1. 创建上下文iocontext
  2. 选择协议
  3. 生成socket
  4. 打开socket

socket要用来通信的时候必须要有一个参数,叫做上下文,上下文是boost_asio的一个核心服务,所有的服务都是通过上下文服务来通信的

int create_tcp_socket() {
	//创建一个socket并打开
	//创建了一个 I/O 上下文对象 ioc。
	// I/O 上下文是 Boost.Asio 中用于处理异步 I/O 事件的核心类
	// 它提供了事件循环、回调管理等功能,用于驱动异步操作。
	asio::io_context ioc;
	//创建了一个 IPv4 的 TCP 协议对象 protocol。
	// asio::ip::tcp::v4() 方法来获取一个表示 IPv4 TCP 协议的对象
	// 以便在后续的套接字创建中使用。
	asio::ip::tcp protocol = asio::ip::tcp::v4();
	//这样可以将套接字与指定的 I/O 上下文关联起来
	// 以便在该上下文中处理套接字的异步 I/O 操作。
	asio::ip::tcp::socket sock(ioc);

	boost::system::error_code ec;
	sock.open(protocol, ec);
	if (ec.value()!= 0) {
		std::cout<< "Failed to parse the IP address.Error code="
			<< ec.value() << ".Message is" << ec.message();
		return ec.value();
	}
}

上述socket只是通信的socket,如果是服务端,我们还需要生成一个acceptor的socket,用来接收新的连接。

int create_acceptor_socket() {
	asio::io_context ios;
	//新写法:生成一个acceptor
	// 为其指定是tcp v4的协议(只接受ipv4的连接)
	// 并且接受所有发往3333端口的连接
	asio::ip::tcp::acceptor a(ios, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 3333));
	//asio::ip::tcp::acceptor acceptor(ios);
	//asio::ip::tcp protocal = asio::ip::tcp::v4();
	//boost::system::error_code ec;//错误码
	//acceptor.open(protocal, ec);
	//if (ec.value() != 0) {
	//	std::cout
	//		<< "Failed to parse the IP address.Error code="
	//		<< ec.value() << ".Message is" << ec.message();
	//	return ec.value();
	//}
	//旧的写法
	return 0;
}

将一个 acceptor 类型的 socket 绑定到指定的端点(endpoint)意味着在服务器端创建了一个监听指定端点的套接字,从而可以接收到针对该端点的传入连接请求。这在网络编程中有以下效果:

接受传入连接:通过将 acceptor 绑定到指定的端点,服务器可以开始监听该端点,并接受传入的连接请求。一旦有客户端尝试连接到该端点,服务器便可以接受并处理这些连接请求。

确定服务器监听的端点:通过绑定 acceptor 到特定的端点,服务器可以指定自己所监听的网络地址和端口号。这样,客户端就知道在哪里可以找到服务器,并且可以向特定的端点发起连接请求。

多端口监听:服务器可以创建多个不同端点的 acceptor,并分别绑定到不同的端口,以便同时监听多个网络端点,从而实现多端口服务或者多协议支持。

总之,将 acceptor 类型的 socket 绑定到特定的端点是启动服务器并开始接受传入连接请求的重要步骤,它为服务器端提供了监听和接受传入连接的能力。

int  bind_acceptor_socket() {

    // Step 1. Here we assume that the server application has
        // already obtained the protocol port number.
    unsigned short port_num = 3333;

    // Step 2. Creating an endpoint.
    asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(),
        port_num);

    // Used by 'acceptor' class constructor.
    asio::io_context  ios;

    // Step 3. Creating and opening an acceptor socket.
    asio::ip::tcp::acceptor acceptor(ios, ep.protocol());

    boost::system::error_code ec;

    // Step 4. Binding the acceptor socket.
    acceptor.bind(ep, ec);

    // Handling errors if any.
    if (ec.value() != 0) {
        // Failed to bind the acceptor socket. Breaking
        // execution.
        std::cout << "Failed to bind the acceptor socket."
            << "Error code = " << ec.value() << ". Message: "
            << ec.message();

        return ec.value();
    }

    return 0;
}

客户端连接到服务端(对端端点)

int connect_to_end() {
	std::string raw_ip_address = "192.168.1.124";
	unsigned short port_num = 3333;
	try {
		asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address),port_num);
		asio::io_context ios;
		asio::ip::tcp::socket sock(ios, ep.protocol());
		//链接这个对端端点
		sock.connect(ep);
	}
	catch (system::system_error& e) {
		std::cout << "Error occured!Error code=" << e.code()
			<< ".Message:" << e.what();

		return e.code().value();
	}
	return 0;
}

服务器端接受连接

int accept_new_connection() {
	const int BACKLOG_SIZE = 30;

	unsigned short port_num = 3333;
	asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(), port_num);
	asio::io_context ios;
	try {
		//似上面的socket,其本质实际上也就是socket
		asio::ip::tcp::acceptor acceptor(ios, ep.protocol());
		//绑定这个对端端点到acceptor接收器上
		acceptor.bind(ep);
		//3.服务器进行监听的操作,不监听就无法接受新的连接
		// 监听的时候要传入监听大小
		acceptor.listen(BACKLOG_SIZE);

		//4.服务器再创建一个Socket,这个Socket跟acceptor是不一样的
		// 这个Socket是用来跟客户端通信的

		asio::ip::tcp::socket sock(ios);

		//5.把接收到的新链接通过这个Socket处理
		acceptor.accept(sock);
	}
		catch (system::system_error& e) {
			std::cout << "Error occured!Error code=" << e.code()
				<< ".Message:" << e.what();

			return e.code().value();
	}
}

1.对于服务器端: 服务器先生成一个端点,并且生成一个acceptor接收器,接收器绑定好端点,也就是服务器本地的地址加上服务器要指定的端口,然后客户端就可以通过这个地址和端口去连接,服务器为了接受他们的连接还要做listen操作,也就是监听,监听好了之后通过accept来返回,accept会返回新的连接,新的连接交给Socket来处理客户端的消息

2.对于客户端: 客户端首先先去连接服务器的地址,通过ip address的这个全局函数form_string()把这个地址转成我们asio要用到的地址,并且传递一个服务器提供的端口号,这样就会生成一个端点,保存了服务器对端的信息,客户端的socket去连接这个信息。创建socket要注意要指定这个socket绑定在哪个服务上,我们要定义一个服务,并且指定一个协议,这个协议就是对端支持的协议。