5-3 网络编程—编写服务端与客户端demo

1. 编写简易客户端与服务端程序

图片说明

1.1 服务端构建流程

如上图所示,使用Socket API建立简易的TCP服务端过程为:

    1. 建立一个socket()
    1. 绑定接受客户端连接的端口 bind()
    1. 监听网络端口 listen()
    1. 等待接受客户端连接 accept()
    1. 向客户端发送一条数据 send()
    1. 接收客户端发送的数据 recv()
    1. 关闭socket()

1.2 服务端源码

//
// Created by Evila on 2021/6/27.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<iostream>
using namespace std;

int main(int argc, char** argv)
{
		//	1. 建立一个Socket
		// 参数 ipv4 面向字节流的 tcp协议
		int _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if( _sock < 0)
		{
				cout << "create socket error: " << strerror(errno) << "%s(errno: " << errno << endl;
				exit(0);
		}

		//	2. 绑定端口45678
		sockaddr_in _sin = {};    // sockaddr_in为网络地址的结构体
		_sin.sin_family = AF_INET;  // 设置协议类型
		int _port = 45678;
		_sin.sin_port = htons(_port); // 设置协议源端口号
		// 计算机数据表示存在两种字节顺序:
		// 网络字节顺序(Network Byte Order, NBO)与主机字节顺序(Host Byte Order, HBO)
		// NBO是大端模式(big-endian),也就是整数的高位字节存放在内存的低地址处
		// 在网络上使用统一的网络字节顺序,可以避免兼容性问题
		// 而主机字节序与CPU或操作系统相关, 无法统一; 因此使用htons()将主机字节序转换成网络字节序
		_sin.sin_addr.s_addr = htonl(INADDR_ANY); // 协议源地址 随机ip
		if (bind(_sock, (sockaddr*)&_sin, sizeof(_sin)) == -1)  //sockaddr 不利于编码
		{
				cout << "ERROR: 绑定用于接受客户端连接的网络端口失败..." << endl;
				exit(0);
		}
		else
		{
				cout << "SUCCESS: 绑定端口" << _port << "成功..." << endl;
		}

		//	3. 监听网络端口 listen
		if (listen(_sock, 5) == -1) // 第二个参数 backbag 为连接为完成队列长度
		{
				cout << "ERROR: 监听用于接受客户端连接的网络端口失败..." << endl;
				exit(0);
		}
		else
		{
				cout << "SUCCESS: 监听端口成功..." << endl;
		}

		//	4. 等待接受客户端连接 accept
		sockaddr_in _clientAddr = {};
		int cliendAddrLen = sizeof(_clientAddr);
		int _clientSock = -1; // 初始化无效的socket 用来存储接入的客户端

		char msgBuf[] = "Hello, I'm Server";
		char recvBuff[2048];

		// 这里为了方便测试 只接受10次连接就关闭
		int n = 10;
		while (n--)
		{
				// 当客户端接入时 accept函数会得到客户端的socket地址和长度
				_clientSock = accept(_sock, (sockaddr*)&_clientAddr, (socklen_t *)&cliendAddrLen);
				if (-1 == _clientSock) //接受到无效接入
				{
						cout << "ERROR: 接受到无效客户端SOCKET..." << endl;
						continue;
				}
				else
				{
						//inet_ntoa 将ip地址转换成可读的字符串
						cout << "新Client加入: IP = " << inet_ntoa(_clientAddr.sin_addr) << endl;
						//	5. 向客户端发送数据 send()
						send(_clientSock, msgBuf, strlen(msgBuf) + 1, 0); // +1是为了把\0算进去
				}

				int recvLen = recv(_clientSock, recvBuff, 2048, 0);
				recvBuff[recvLen] = '\0';   // 设置字符串结束符
				cout << "recv msg from client: " << recvBuff << endl;
		}

		// 7. 关闭socket
		close(_sock);
		return 0;
}

1.3 编译并运行

使用g++命令编译并生成可执行文件server,执行后该进程阻塞在accept()函数处。 图片说明

1.4 客户端源码

//
// Created by Evila on 2021/6/27.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char** argv)
{
		// 1. 建立一个Socket
		int _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (_sock < 0)
		{
				cout << "create socket error: " << strerror(errno) << "%s(errno: " << errno << endl;
				exit(0);
		}

		// 2. 设置请求连接的server 地址
		sockaddr_in _sevrAddr = {};    // sockaddr_in为网络地址的结构体
		_sevrAddr.sin_family = AF_INET;
		int port = 45678;
		_sevrAddr.sin_port = htons(port);

		string server_ip;
		cout << "please input server ip: ";
		cin >> server_ip;

		if (inet_pton(AF_INET, server_ip.c_str(), &_sevrAddr.sin_addr) <= 0)
		{
				cout <<"inet_pton error for " << server_ip << endl;
				exit(0);
		}

		// 3. 连接服务端
		int ret = connect(_sock, (struct sockaddr*)&_sevrAddr, sizeof(_sevrAddr));
		if (ret < 0)
		{
				cout << "connect error: " << strerror(errno) << "errno: " << errno << endl;
				exit(0);
		}
		else
		{
				cout << "connect to server: " << server_ip << ":" << port << " success!" << endl;
		}

		// 4. 向服务端发送数据
		

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++岗面试真题解析 文章被收录于专栏

<p> C++工程师面试真题解析! </p> <p> 邀请头部大厂创作者<a href="https://www.nowcoder.com/profile/73627192" target="_blank">@Evila</a> 及牛客教研共同打磨 </p> <p> 助力程序员的求职! </p>

全部评论

相关推荐

头像
11-18 16:08
福州大学 Java
影流之主:干10年不被裁,我就能拿别人一年的钱了,日子有盼头了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务