前言
本文主要学习Linux内核编程,结合Visual Studio 2019进行跨平台编程,内容包括网络编程基础知识,以及服务器和客户端的案例编程及测试
网络编程三要素
- IP地址:主机(电脑)的标识,类似家庭住址
- 通信协议:双方实体完成通信或服务所必须遵循的规则和约定
- 端口号:每个应用程序都对应一个端口号,类似于家门口的门牌
1.IP地址
- 在网络中想要与其他的机器进行通信就需要要有一个自己的IP地址
🌰举个例子:
客户在某购物平台下单,商家需要发货给你,这个过程当中有两个重要的部分
其一,需要知道客户的地址,不然没办法送货到客户手上
其二,需要通过快递公司来进行送货派件
如下图所示:
同理可得,网络编程也是需要优先知道IP地址
客户端,首先需要知道服务器的IP地址,然后与其进行连接,之后再与其他的客户端进行通信
如下图所示:
在我们日常生活中,常用到域名搜索,其实也可以利用用IP地址进行搜索,怎么操作呢?
首先,我们打开电脑的终端(cmd),可以试试看,在网页网址输入区输入IP能否到达目标页面
输入命令如下:
ping www.baidu.com
这边就是我们百度的IP地址了,如下图所示:
打开我们的网址,试试看通过IP进行搜索,同样也能打开我们的百度界面,如下图所示:
因此,我们可以发现 域名 = IP地址 (www.baidu.com = 14.215.177.39)
思考一下,为么会这样呢?
其实,域名和IP地址是一个键值对,是唯一确定的
为什么在我们在生活中,一般不用IP地址进行搜索呢?很简单,需要记住这么多没规则的数字是不是比较难?所以,我们用域名进行搜索会更加的方便!
使用域名的好处:
- 方便用户记忆
- 保护IP地址,防止暴露,遭受攻击(平常隐藏起来)
2.通信协议
- 可以简单地理解为各计算机之间进行相互会话所使用的共同语言
- 两台计算机在进行通信时,必须使用通信协议
🌰举个例子:
人与人之间沟通,需要使用同一种语言进行对话,不然就会听不懂
两个人互相打招呼,一个人用日语,一个人用英语,使用的是不同的语言
在他们只会本国语言的前提下,是听不懂对方在说什么的
所以需要统一为同种语言,英语或者日语,才能方便交流
如下图所示:
这边着重展开介绍下,以下两种协议:
TCP/IP
在TCP/IP网络体系结构中,TCP、UDP 是传输层最重要的两种协议,为上层用户提供级别的通信可靠性
- TCP(传输控制协议):是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议
- UDP(用户数据报协议) :是在OSI(开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
- 应用层 : HTTP 、FTP、SNMP等
- 表示层 : ASCII、PICT、TIFF、JPEG、MIDI、MPEG
- 会话层 : RPC、SQL、NFS.X WINDOWS、ASP
- 传输层 : TCP、UDP
- 网络层 :IP、IPX、APPLETALK、ICMP
- 数据链路层 : 802.2、802.3ATM
- 物理层 :略
TCP和UDP的区别
TCP和UDP的区别
TCP
UDP****传输介质流式IO数据报文包传输限制理论上无限制每次64KB速度慢快传输可靠性可靠不可靠是否连接验证通信双方是否连接不需要验证是否连接应用场景传输少量数据传输大量数据特点总结牺牲效率,提高数据安全性牺牲部分数据安全性,提高效率
3.端口号
- 用于区分一台主机中接收到的数据包应该转交给哪一个进程进行处理
简而言之,我们得到对方机器的IP地址后需要与对方机器的具体哪一个程序进行通信
需要一个具体的标识,类似虽然知道你的家庭地址,也要找到你家的门牌子
才能确定," 啊对对对,就是这!" 我们的门牌,如下图所示:
网络编程基础
SOCKET概述
- 所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象
- 一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制
- 从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口
SOCKET分类
- 流式套接字(SOCK_STREAM)
流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议
TCP 保证了数据传输的正确性和顺序性
- 数据报套接字(SOCK_DGRAM)
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的
并且不保证可靠,无差错。使用数据报协议UDP协议
- **原始套接字 **
原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等
以上,便是网络编程三要素和SOCKET套接字的相关知识的介绍,接下来将会利用TCP协议来建立服务器和客户端之间的关系,通过实际的案例来加深上述知识点,以及能够对其实际使用场景有一个清楚直观的认识
1.编程思路
- 通过设计read读端、write写端,设计客户端和服务器,在对读端和写端进行通信操作
整体编程思路,如下流程图所示:
基于流式套接字的编程流程
2.建立本地服务器
socket()函数
- 用途:用于网络初始化,返回文件描述
函数原型:
int socket(int domain, int type, int protocol);
返回值:
成功:返回文件描述符
失败:返回 -1
bind()函数
- 用途:用于绑定IP地址和端口号,以及确定通信协议为ipv4
函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:
成功:返回 0
失败:返回 -1
listen()函数
- 用途:用于监听是否有客户端进行连接
函数原型:
int listen(int sockfd, int backlog);
返回值:
成功:返回 0
失败:返回 -1
accept()函数
- 用途:用于等待客户端连接,是一个阻塞式函数
函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:
成功:返回客户端文件描述符
失败:返回 -1
服务器完整代码
#include <iostream>
#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>
using namespace std;
//服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器
//服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器
//服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器
//服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器
//服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器-服务器
int main()
{
int socketfd = 0;
int acceptfd = 0;
int len = 0;
int pid = 0;
char buf[255] = { 0 };//存放客户端发过来的信息
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == -1)
{
perror("socket error");//socket 报错
}
else
{
struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET;
//默认提供IP地址(服务器自己)因为服务器是被动,需要客户端来连接所以网路通道打开是服务器自己
s_addr.sin_addr.s_addr = inet_addr("192.168.48.128");
//端口号(服务器自己)大小端模式 内存存储字节转换
s_addr.sin_port = htons(10086); //端口号为10086
len = sizeof(s_addr);
//绑定IP地址和端口号
int res = bind(socketfd, (struct sockaddr*)&s_addr, len);
if (res == -1)
{
perror("bind error");//bind 报错
}
else
{
//监听:地址和端口有没有客户端连接上
if (listen(socketfd, 2) == -1)
{
//listen 监听失败
perror("listen error");
}
cout << "socket 网络搭建成功" << endl;
//设置死循环:保证服务器一直在线
while (1)
{
cout << "稍等片刻-等待连接-客户端" << endl;
//阻塞式函数 acceptfd
acceptfd = accept(socketfd, NULL, NULL);
cout << "恭喜恭喜-连接成功-客户端 socketfd = " << acceptfd << endl;
pid = fork();//创建子进程 子进程:保持运行提供服务到结束
if (pid == 0)
{
while (1)
{
int res = read(acceptfd, buf, sizeof(buf));
cout << "pid = " << getpid() << "res = "<< res << endl;
cout << " 输入内容: " << buf << endl;
bzero(buf, sizeof(buf));
}
}
}
}
}
}
3.建立客户端
**客户端完整代码 **
#include <iostream>
#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>
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;
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = inet_addr("192.168.48.128");
s_addr.sin_port = htons(10086); //端口号为10086
len = sizeof(s_addr);
//绑定IP地址和端口号
int res = connect(socketfd, (struct sockaddr*)&s_addr, len);
if (res == -1)
{
perror("connect error");//连接失败
}
else
{
cout << "connect success" << endl;//连接成功
while (1)
{
fgets(buf, sizeof(buf), stdin);//控制台输入
write(socketfd, buf, sizeof(buf));
bzero(buf, sizeof(buf));
}
}
}
return 0;
}
4.代码测试
博主是利用Linux和vs的跨平台开发,先在VS上重新生成解决方案
然后在Linux上打开对应工程,提供两种方法,如下:
- 通过debug文件下.out文件直接执行
- 或者g++ main.cpp -o xxx 生成可执行文件
方法一:执行.out文件
方法二:g++ main.cpp -o f ( f 这边自定义)
PS:博主这边main函数打错了,按照上述格式来即可
先打开服务器,再打开客户端
打开服务器成功!如下图所示:
连接上客户端1成功! 如下图所示:
连接上客户端2成功! (重复打开客户端1操作)如下图所示:
开始输入测试,可以看到我们在客户端1和客户端2输入,我们的服务器都会有显示
参考:linux远程开发——网络通信(客户端与服务器建立连接)_似末的博客-CSDN博客_在linux下如何建立客户端与服务器
以上就是本文的全部内容啦!如果对您有帮助,麻烦点赞啦!收藏啦!如有疑问请在评论区留言!
版权归原作者 猿力猪 所有, 如有侵权,请联系我们删除。