当前课程知识点:基于Linux的C++ > 第十五讲 网络编程 > 15.4 套接字(二) > LinuxCPP1504
我们看具体的实现代码
我们这里边使用本地套接字
实现服务器端和客户端
看看服务器端是怎么实现的
我们实现一个函数
Serve() 服务函数
返回值是一个bool量
它接受是一个客户套接字的一个参数
这个函数呢将负责持续地读取信息
直到这个套接字被关闭
或者连接这个套接字的那个客户
发出“quit”这个消息
如果你没有发出“quit”消息
或者这个套接字没关闭那么
这个服务器就不断地
从套接字里把这个信息拿到
它就认为里面你可能有信息
它就不断地朝外拿信息
就这个意思
如果是套接字被关闭了
那么返回值就是true
如果是用户
发出了一个“quit”这个请求
我们的返回值就设成false
就是说我不服务了
函数本身内部主代码是while( true )
它是个无限循环
不断地从里面取数据
它不停的 永远不停的
除非套接字被关了
除非用户说我不玩了 quit
否则它不返回的
while( true ) 从里面取数据
通过read()这个系统调用
从这个套接字里边
把这个数据取出来
如果返回值是0
说明这个套接字被关闭了
我们就return true
这个函数就算做完了
但是我们这个服务器并不能关 对吧
就这个意思
取到这个数据之后
我们就会把它输出
然后看用户输入的那个数据
是不是“quit”
如果是“quit”我就不玩了
我就return false
Serve()就算结束了
套接字的服务就算结束了
任何一个客户发出一个“quit”消息
这个套接字服务器就关掉
就这个意思
定义一个套接字地址的
一个结构体的一个变量
定义一个name
这是一个套接字地址的名
定义一个serving这样的一个变量
用来表达服务器是不是持续在运行
然后我们调用socket()创建这个套接字
使用PF_LOCAL
表明创建的是一个本地套接字
设定好这个服务器的具体的性质
把它路径啊什么的都设定号
然后绑定这个套接字
调用bind()函数把这个套接字fd
和这个地址给绑定起来
这个就算把本地服务器
和套接字给挂上了
绑定了完之后
我们侦听客户的连接
就看有没有客户
通过我们这个套接字
向这边发送消息了
侦听到了之后
我们重复地接受这个连接
直到某个客户发出“quit”的消息
我才over 我才结束
所以这个主体循环就是while( serving )
当它是处于服务状态的时候
那么定义一个客户的地址
定义一个客户的套接字的文件描述符
然后我们就接受这个客户的连接
形成一个客户的套接字fd
在此之后 提供这个服务
从那个客户中读取数据
或者向那个客户中发送数据
都可以通过Serve()这个函数去做了
做完之后 关闭掉这个套接字
这次连接就算结束了
所以我们就关闭这个套接字
然后进一步去循环
看是不是继续要提供服务
如果是 我们就继续地接受它的服务
然后提供服务 然后关闭
继续 循环
它是按照这个模式
除非一个客户连接发出一个“quit”请求
当接受到“quit”请求之后
我就停止服务
最后别忘了close我们的套接字
关闭我们的套接字文件描述符
最后别忘了用unlink
删除套接字文件
这是本地套接字设立的服务器端
我们还需要写一个连接它的客户端
在客户端里面
我实现一个函数叫SendMsg()
发送消息
就向一个对应的套接字里边
发送一个特定的字符串
写一个数据进去
函数实现很简单
这我们就不解释了
我们看主函数
我仍然需要创建一个套接字
socket( PF_LOCAL, SOCK_STREAM, 0 )
然后设定好它的存储服务器的名字
在套接字地址里边设定好它的名字
设定好名字之后 连接
调用connect()去连接那个服务器
传设定的信息 连接进去
连接成功 发送消息 SendMsg()
就向它发送 就OK了
发送完close() 关闭
这就是我们的客户端
这两个程序你分别地编译
生成可执行文件
那么我们怎么运行它呢
那么你分别编译
客户端和服务器端两个程序
形成两个可执行文件
然后你进入服务器端的那个程序的目录
在终端里面就输入“./server /tmp/socket”
这样的话
就会启动这个套接字的服务器
并且在“/tmp”这个目录下
给你构造一个套接字的文件
服务器和客户端
就会通过这个文件进行通讯
就是这个意思
“./server”就是那个服务器端的程序名字
“/tmp/socket”就是这个本服务器
启动以后的套接字文件名
然后你进入客户端的程序目录
在一个新的终端里面输入
“./client /tmp/socket”
后面跟着一个字符串“Hello World!”
什么意思呢
就是启动我们这个客户端
向套接字“/tmp/socket”
里边输入一串信息“Hello World!”
那么当这个服务器接受到
这个客户的连接请求之后
它接受到了这个字符串
它就会把这个字符串
在屏幕上输出出来
表示它接受到了这个信息
如果你要停止这个服务器
就在客户端里面输入命令
“./client /tmp/socket”
后面输出一个命令串“quit”退出
那个套接字就会停止
这是我们的本地套接字的实现
接下来一个例子呢
就是网络的实现
我们这里边
给出网络套接字的客户端的实现
服务器端的实现我们没有给出来
那是我们最后的大作业
你要做的事
网络套接字和本地套接字
理论上讲实现上几乎没有差别
它就是那个属性的设置的问题
剩下的基本上没有差别
只不过因为我们网络上数据的传输
大部分的时候
我们要生成HTML文档
那个时候生成HTML文档
或解析HTML文档会比较麻烦一点
其实除此之外和本地套接字的实现
基本上差不多
步骤是非常非常类似的
首先 我们可以写一个函数叫GetHomePage()
来获取Web服务器的主页
我们这里边只是简单地
向它发送一个“GET /”命令
就是“GET /\n”
你就把这个命令发到了Web服务器上
Web服务器接受到这个命令以后
就会把它的主页内容传递给你
就这个意思
那么我们在这里边
就向它套接字里写入这个请求
写入这个请求之后
立即就从套接字里边
把它反馈的信息读出来
我们无限地读
做了一个无限循环去读它
一直读 一直读
只要有信息 我们就把它读出来
就这个意思
我们读出来以后
我们就把它呈现出来
你可以向标准输出流里面输出
你可以向文件里输出
你可以弄了一个网页浏览器
然后就在它那个工作区
或者那个画布
就在它的工作区里边把它绘制出来
总之你读到了这个信息
你想用什么方式处理它
就按什么方式处理它
如果读到的数据是0
就说明什么呢
说明没有数据可读了
所以我们就return
就获取它的主页
接下来就是网络套接字的
那个客户端的主函数
在实现上实际上和
那个本地套接字的客户端非常得相似
但只不过呢
因为我们这里面要访问是网络资源
所以它实际上是用
网络的那个地址传递信息的
所以这个地方设计的时候
会有一些额外的函数调用
我们会调用socket()
创建这样的一个套接字
然后设定它的网络传输属性
这是PF_INET、AF_INET
不是PF_LOCAL、AF_LOCAL了
这是网络套接字
不是本地套接字了
然后呢 传一个参数进来了argv[1]
就是你执行程序的时候
你要把那个HTTP请求
那个HTTP的地址
那个网址作为这个程序(客户端程序)
的参数传进来的
所以我们这里调用gethostbyname()
得到那个套接字的主机的名字
然后还调用htons()得到它的端口
设定它的端口
我们主机端口一般我们设成80
OK 就这个意思
设定它的端口
然后我们connect()
连接那个套接字
如果成功地连接
哎呀 没啥可说的
我们就读取它的信息就行了
调用GetHomePage()
获取它的主页就完了
如果没有成功连接
那就是网站不能用
没有提供这个服务
诸如此类 不管怎么样
那我们就退出 这是很典型
这就是网络套接字的客户端
我这个程序运行以后
我们就尝试着用它去访问了
www.tsinghua.edu.cn这个网站
然后它返回了HTML的文本
就这个模样
因为这是一个文本界面的Web客户端
所以你也指望
给你显示什么图形啊什么的
那些东西都不存在
只有最简单的文本信息
所以这个网页超级简单
就是什么
最基本的报头信息
找到了这个文档
然后告诉你
它是清华Web服务器1.2.1
就这么最简单的信息传过来了
但不管怎么样
这个网址我们访问到了
它确实反馈回来了信息
这就是我们的
网络套接字的客户端的实现
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口