linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)

目录
一、前言
二、网络编程三要素
1、IP地址
1)IP地址概念
2)通过IP地址访问CSDN官网
3)本地回环IP地址 127.0.0.1
2、端口号
3、通信协议
1)通信协议概念
2)TCP和UDP
三、网络通信基础编程
【linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)】1、编程流程
2、建立本地服务器
1)socket()初始化网络
2)bind()函数
3)listen()监听函数
4)accept()函数
5)服务器全部代码
3、建立客户端
4、客户端连接服务器测试

一、前言

本文介绍网络编程的基础知识,使用 Visual Studio 2019 在 linux 本地搭建一个服务器,将客户端与本地服务器连接起来,通过客户端向服务器发送信息,测试服务端能否收到信息。
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片


  • 在编程之前我们先来了解一些基础知识
二、网络编程三要素
网络编程三要素由下面3点组成:
1、IP地址:确定网络中某台计算机的位置
2、端口号:确定是哪个具体的应用程序
3、通信协议:通信双方共同约定和遵循的协议
1、IP地址 1)IP地址概念
  • IP地址是用来区分是哪一台电脑设备,只有知道对方设备的IP地址,才能找到对方的电脑。
IP地址就像是我们的家庭住址一样,如果你要写信给一个人,你就要知道他(她)的地址,这样邮递员才能把信送到。计算机发送信息就好比是 “邮递员”,它必须知道唯一的 “家庭地址”才能不至于把信送错人家。
同样的网络编程也需要用到IP地址,客户端需要知道服务器的IP地址,才能找到需要建立连接的服务器。其实网页也有它对应的IP地址,我们平常搜索东西都是输入域名来访问,也可以输入IP地址进行访问。
2)通过IP地址访问CSDN官网
比如:csdn的官网为 www.csdn.net
  • window 下通过按键 win+r 打开运行框输入 cmd 然后回车
  • 控制台输入 pingwww.csdn.net
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

  • 浏览器输入 39.106.226.142 也可以访问到CSDN官网
可以发现域名后面跟着一个IP地址,浏览器输入这个IP地址也可以访问到CSDN官网。实际上域名和IP地址是一个键值对,是唯一的。如果为了访问CSDN官网要输入那么长的IP地址是不是不方便,所以这时候域名就出现了,用域名来访问是不是更加方便。
3)本地回环IP地址 127.0.0.1
  • 本地服务器的IP地址就可以设置成127.0.0.1
127.0.0.1,通常被称为本地回环地址 (Loopback Address),不属于任何一个有类别地址类。 它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。 在Windows操作系统中也有相似的定义,所以通常在安装网卡前就可以ping通这个本地回环地址。
2、端口号
  • 如果仅仅知道IP地址只能找到具体的某一台计算机,要找到某个应用程序需要知道端口号
简单来说端口号就是正在运行的程序的标识,用来区分具体是那个应用程序。
3、通信协议 1)通信协议概念
  • 建立通信还需要通信协议,就比如中国统一的语言汉语,这样沟通起来就轻而易举了
通信协议是指双方实体完成通信或服务所必须遵循的规则和约定。通过通信信道和设备互连起来的多个不同地理位置的数据通信系统,要使其能协同工作实现信息交换和资源共享,它们之间必须具有共同的语言。交流什么、怎样交流及何时交流,都必须遵循某种互相都能接受的规则。这个规则就是通信协议。
2)TCP和UDP
  • 目前成熟的通信协议是TCP和UDP,其他协议的前身都是TCP或者是UDP
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

  • 来看一下两个协议的区别
TCP UDP
是否需要建立连接 需要建立连接,才能发送信息 不需要建立连接
传输介质 流式IO(二进制数据) 数据封装成报文包
传输限制 可以进行大数据传输 每次只能传输64KB
总结 牺牲效率,提高传输安全性,可靠协议 牺牲部分安全性,提高传输效率,不可靠协议
  • OSI参考模型与TCP/IP参考模型如下图所示
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

接下来我们通过代码使用 TCP 协议来建立服务器与客户端之间的联系。
话不多说
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

三、网络通信基础编程 1、编程流程
  • 基于流套接字的编程流程如下图所示
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

本文介绍客户端与服务器建立联系之后,客户端write (),服务器read (),先做简单的连接测试,后期再进行客户端与服务器进行互相通信。接下来的编程按照上面的流程进行。
2、建立本地服务器 1)socket()初始化网络
int socketfd = 0; //初始化网络参数一:使用ipv4参数二:流式传输 socketfd = socket(AF_INET, SOCK_STREAM, 0);

  • socket()网络初始化 ,返回文件描述符
函数原型:
int socket(int domain, int type, int protocol);
返回值:
成功:返回文件描述符
失败:返回 -1
  • 第一个参数用AF_INET ,即 ipv4
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

  • 第二个参数用SOCK_STREAM ,即流式的套接字
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

  • 第三个参数为0即可
2)bind()函数
  • 绑定ip地址和端口号,以及确定通信协议为ipv4
//原本使用struct sockaddr,通常使用sockaddr_in更为方便,两个数据类型是等效的,可以相互转换 struct sockaddr_in s_addr; //确定使用哪个协议族ipv4 s_addr.sin_family = AF_INET; //系统自动获取本机ip地址 也可以是本地回环地址:127.0.0.1 s_addr.sin_addr.s_addr = INADDR_ANY; //端口一个计算机有65535个10000以下是操作系统自己使用的,自己定义的端口号为10000以后 s_addr.sin_port = htons(12345); //自定义端口号为12345len = sizeof(s_addr); //绑定ip地址和端口号 int res = bind(socketfd, (struct sockaddr *)&s_addr, len); if (res == -1) { perror("bind error"); }

函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:
成功:返回 0
失败:返回 -1

  • 第一个参数用上一步返回的文件描述符
  • 第二个参数改用 struct sockaddr_in 结构体,更方便
  • 第三个参数为 struct sockaddr_in 结构体的大小
3)listen()监听函数
  • 监听有没有客户端来连接
//监听这个地址和端口有没有客户端来连接第二个参数现在没有用只要大于0就行 if (listen(socketfd, 2) == -1) { perror("listen error"); }

函数原型:
int listen(int sockfd, int backlog);
返回值:
成功:返回 0
失败:返回 -1

  • 第一个参数为之前获取的服务器文件描述符
  • 第二个参数只要 >0 即可
4)accept()函数
  • 等待客户端上线,阻塞式函数 ,只有客户端上线才会继续执行
通过一个while(1)死循环,让服务器一直在线等待客户端上线,一旦某个客户端上线,则开一个子进程,在子进程中循环读取客户端发过来的消息。父进程则继续等待下一个客户端上线。
注:
read()函数也是一个阻塞函数。
//死循环保证服务器一直在线 while (1) { cout << "等待客户端上线" << endl; //等待客户端上线,阻塞式函数acceptfd为连上来的客户端fd acceptfd = accept(socketfd, NULL, NULL); cout << "客户端上线 fd = " << acceptfd << endl; pid = fork(); if (pid == 0)//子进程持续读取客户端发来的信息 { while (1) { read(acceptfd, buf, sizeof(buf)); cout << "pid = " << getpid() << " 说: " << buf << endl; bzero(buf, sizeof(buf)); } } }

函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:
成功:返回客户端文件描述符
失败:返回 -1
  • 第一个参数为之前获取的服务器文件描述符
  • 第二个第三个参数可以指定为NULL
5)服务器全部代码
#include #include #include #include #include #include #include using namespace std; int main() { int socketfd = 0; int acceptfd = 0; int len = 0; int pid = 0; char buf[255] = { 0 }; //存放客户端发过来的信息 //初始化网络参数一:使用ipv4参数二:流式传输 socketfd = socket(AF_INET, SOCK_STREAM, 0); if (socketfd == -1) { perror("socket error"); } else { //原本使用struct sockaddr,通常使用sockaddr_in更为方便,两个数据类型是等效的,可以相互转换 struct sockaddr_in s_addr; //确定使用哪个协议族ipv4 s_addr.sin_family = AF_INET; //系统自动获取本机ip地址 也可以是本地回环地址:127.0.0.1 s_addr.sin_addr.s_addr = INADDR_ANY; //端口一个计算机有65535个10000以下是操作系统自己使用的,自己定义的端口号为10000以后 s_addr.sin_port = htons(12345); //自定义端口号为12345len = sizeof(s_addr); //绑定ip地址和端口号 int res = bind(socketfd, (struct sockaddr *)&s_addr, len); if (res == -1) { perror("bind error"); } else { //监听这个地址和端口有没有客户端来连接第二个参数现在没有用只要大于0就行 if (listen(socketfd, 2) == -1) { perror("listen error"); } //死循环保证服务器一直在线 while (1) { cout << "等待客户端上线" << endl; //等待客户端上线,阻塞式函数acceptfd为连上来的客户端fd acceptfd = accept(socketfd, NULL, NULL); cout << "客户端上线 fd = " << acceptfd << endl; pid = fork(); if (pid == 0)//子进程持续读取客户端发来的信息 { while (1) { read(acceptfd, buf, sizeof(buf)); cout << "pid = " << getpid() << " 说: " << buf << endl; bzero(buf, sizeof(buf)); } } } } } }

3、建立客户端
  • 客户端这边只要做socket()初始化网络,并绑定ip地址和端口号,确定通信协议ipv4
#include #include #include #include #include #include #include #include using namespace std; int main() { int socketfd = 0; int acceptfd = 0; int len = 0; char buf[255] = { 0 }; //初始化网络 socketfd = socket(AF_INET, SOCK_STREAM, 0); if (socketfd == -1) { perror("socket error"); } else { struct sockaddr_in s_addr; //确定使用哪个协议族ipv4 s_addr.sin_family = AF_INET; //填入服务器的ip地址也可以是127.0.0.1 (回环地址) s_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //端口一个计算机有65535个10000以下是操作系统自己使用的,自己定义的端口号为10000以后 s_addr.sin_port = htons(12345); //自定义端口号为12345len = sizeof(s_addr); //绑定ip地址和端口号 int res = connect(socketfd, (struct sockaddr*)&s_addr, len); if (res == -1) { perror("connect error"); } else { while (1) { //控制台输入 fgets(buf, sizeof(buf), stdin); write(socketfd, buf, sizeof(buf)); bzero(buf, sizeof(buf)); } } } return 0; }

注:
1、因为是本地的客户端,且服务器也是本地的,所以绑定的ip地址为本地的回环地址即可
2、绑定的端口号和使用的通信协议必须和服务器一样
3、write()函数中的文件描述符为服务器的
4、客户端连接服务器测试
  • 在linux下找到对应的main.cpp ,通过g++ 的方式编译, ./ 运行
注:
1、代码有改动的话,需要重新生成解决方案,代码即可同步到linux下
2、先运行服务器,再运行客户端
测试结果
linux远程开发|linux远程开发——网络通信(客户端与服务器建立连接)
文章图片

可以发现每运行一个客户端,服务器都会收到提示,而且客户端发送的信息,服务器都可以收到,说明客户端和服务器连接成功!!

原创不易,转载请标明出处。
对您有帮助的话可以一键三连,会持续更新的(嘻嘻)。


    推荐阅读