搭建C/S:用C++搭建一个最简单的,基于socket网络编程的客户端和服务器
1. 搭建C/S
本节主要讲述如何使用C++搭建一个简单的socket服务器和客户端。
为了能更加容易理解如何搭建,本节会省略许多细节和函数解释,对于整个连接的过程的描述也会比较抽象,细节和解析会留到之后再讲。
(1)服务端和客户端的预期功能
这里要实现的服务端的功能十分简单,只需要把任何收到的数据原封不动地发回去即可,也就是所谓的ECHO服务器。
客户端要做的事情也十分简单,读取用户输入的一个字符串并发送给服务端,然后把接收到的数据输出出来即可。
服务端搭建
将上面的需求转化一下就可以得到如下形式:
while(true)
{
buff = 接收到的数据;
将buff的数据发回去;
}
当然,上面的伪代码是省略掉网络连接和断开的过程。这个例子使用的连接形式为TCP连接,而在一个完整的TCP连接中,服务端和客户端通信需要做三件事:
- 服务端与客户端进行连接
- 服务端与客户端之间传输数据
- 服务端与客户端之间断开连接
将这些加入伪代码中,便可以得到如下伪代码:
while(true)
{
与客户端建立连接;
buff = 接收到从客户端发来的数据;
将buff的数据发回客户端;
与客户端断开连接;
}
首先需要解决的就是,如何建立连接。
在socket编程中,服务端和客户端是靠socket进行连接的。服务端在建立连接之前需要做的有:
- 创建socket(伪代码中简称为
socket()
) - 将socket与指定的IP和端口(以下简称为port)绑定(伪代码中简称为
bind()
) - 让socket在绑定的端口处监听请求(等待客户端连接到服务端绑定的端口)(伪代码中简称为
listen()
)
而客户端发送连接请求并成功连接之后(这个步骤在伪代码中简称为accept()
),服务端便会得到客户端的套接字,于是所有的收发数据便可以在这个客户端的套接字上进行了。
而收发数据其实就是:
- 接收数据:使用客户端套接字拿到客户端发来的数据,并将其存于缓冲区buf中。(伪代码中简称为
recv()
) - 发送数据:使用客户端套接字,将buff中的数据发回去。(伪代码中简称为
send()
)
在收发数据之后,就需要断开与客户端之间的连接。在socket编程中,只需要关闭客户端的套接字即可断开连接。(伪代码中简称为close()
)
客户端进行的操作:绑定、监听、连接、收发数据
将其补充进去得到:
sockfd = socket(); // 创建一个socket,赋给sockfd
bind(sockfd, ip::port和一些配置); // 让socket绑定端口,同时配置连接类型之类的
listen(sockfd); // 让socket监听之前绑定的端口
while(true)
{
connfd = accept(sockfd); // 等待客户端连接,直到连接成功,之后将客户端的套接字返回出来
recv(connfd, buff); // 接收到从客户端发来的数据,并放入buff中
send(connfd, buff); // 将buff的数据发回客户端
close(connfd); // 与客户端断开连接
}
这便是socket服务端的大致流程。
下面利用vscode和Linux在终端中建立一个服务端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
using namespace std;
int main() {
int server = 0;
struct sockaddr_in saddr = {
0};
server = socket(AF_INET, SOCK_STREAM, 0);
if (server == -1){
cout << "server socket error" << endl;
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1){
cout << "server bind error" << endl;
return -