socket基本概念
socket又称为套接字,是一组接口,本身可以理解为一个函数,且对于linux系统而言,它可以被看作是一个文件描述符。那么这个接口具体是哪里的接口呢?又有什么作用呢?
socket与TCP/IP协议
典型的网络应用往往是由以对程序组成的,它们位于两个不同的端系统中,当运行这两个程序时会创建一个客户进程和一个服务器进程,且这两个进程往往运行在不同的主机上。
但是由于每个主机系统都有各自命名进程的方法,且互相往往是不兼容的,这时两个不同系统的主机上的进程就相当于由于语言不同无法沟通的两个不同国家的人一样,所以为了能够进行使得不同系统上的进程可以顺利进行通信,需要引入一套统一的标准,这就相当于秦始皇统一度量衡和语言一样。这就引出了我们的网络协议标准模型——OSI七层模型,或者说时我们的TCP/IP网络协议栈(传输控制协议),具体如下图所示:
以上的每一层都有其对应的协议,在上图中也作标记,而socket也就位于应用层和传输层之间,它可以看作是用户进程与传输层的中间人,如下图所示:
这样的标准使得用户只需要针对socket进行编程即可,不需要研究传递的消息在网络层在光缆上具体如何传输,这也是分层带来的好处之一。
这里需要注意的是由于传输层协议主要是TCP和UDP协议,所以作为应用层和传输层的中间人,socket编程也分为了面向TCP和面向UDP两类。关于TCP和UDP的具体知识非常繁杂,本文就不特别详解这部分的知识了,后续关于这两大协议的知识将会直接作为补充向大家介绍。
socket五要素
socket在本质上是为了不同主机上的进程能够进行通信,就像前文中我们说到往往分为两个进程,即服务器进程和客户端进程,那么在发送相关数据时,往往需要确定是发往哪台主机中的哪个端口,传输过程中服从哪种协议。
我们可以使用发快递来类比理解,卖家可以理解为服务器进程,买家即是客户端进程,发送一批货物首先需要备注好卖家信息和买家信息,其中的本地地址即是卖家店铺所在地址,端口号就相当于卖家名字,同样的远程地址即是买家地址,端口号即是买家名字。而协议大家可以理解为指定快递运送过程中的快递公司。这样就可以将快递从卖家手上发送到买家手上。
宗接下来,socket具有了以下的五要素:
- 协议
- 本地地址
- 本地端口
- 远程地址
- 远程端口
接口流程图
关于TCP的客户端和服务端接口流程图如下图所示:
编写客户端
如我们给出的图,按顺序运行相关函数,首先创建一个socket,然后客户端和服务器端进行连接,连接后就可以读写数据,使用结束后关闭该socket即可。
具体代码如下,其中备注代码相关细节解析:
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
int main(){
// 创建socket
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == fd){
perror("socket error");
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET; // 协议族
// IP地址对应的长整数
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 端口号是服务器端的端口号
addr.sin_port = htons(8088); // 将主机序转为网络序
// 做与服务器的连接
// 需要知道服务器的IP地址
int res = connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));;
if(-1 == res){
// 创建链接返回-1为失败
perror("socket error");
return 1;
}
// 写数据
string msg;
cin >> msg;
// 发送数据用write
write(fd, msg.c_str(), msg.size()+1);
// 读数据
char buff[200];
read(fd, buff, 200);
cout << buff << endl;
// 关闭socket
close(fd);
}
编写服务器
如我们给出的图,按顺序运行相关函数,具体代码如下,其中备注代码相关细节解析:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
int main(){
// 为服务端创建端口号
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == listenfd){
perror("socket error");