进程间的通信有:管道、信号、system V ipc对象通信号灯集
前面的通信都局限于本主机,而当跨主机通信时就操作不了。
其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
1、相关关键字和历史
(1)套接字
所谓【套接字】(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口
(2)相关历史:
? 第一阶段:ARPAnet(阿帕网),是网络基础协议得雏形
? 第二阶段:第一份IP协议说明书
? 第三阶段:TCP/IP协议被制定
(3)网络程序设计框架:C/S、B/S
? <1>C/S:表示客户端/服务器设计框架
- ? 优点:支持本地缓存(能实现较好的效果)、应用层协议可以自己灵活制定
- ? 缺点:开发工作量较大、用户安全性较低
? <2>B/S:表示浏览器/服务器设计框架
- ? 优点:开发工作量较小、用户安全性较高
- ? 缺点:不支持本地缓存、应用层协议必须遵守http协议
(4)网络协议分层模型:
OSI分层模型:理想的网络分层模型
- ? 应用层
- ? 表示层
- ? 会话层
- ? 传输层
- ? 网络层
- ? 数据链路层
- ? 物理层
TCP/IP分层模型:一种标准的网络分层模型
- ? 应用层:http协议、tftp协议、NFS协议。。。
- ? 传输层:TCP协议、UDP协议。。。
- ? 网络层:IP协议、ICMP协议。。。
- ? 物理接口层:以太网协议、ARP协议、RAPP。。。
eg:例如我们向另一台主机发送”还钱“ 总结:
1.应用层:可以由用户自定义的协议,方便数据传输以及数据加密 2.传输层:提供端对端的数据传输 3.网络层:提供数据的路由 4.网络接口层:实现网络底层驱动
2、网络接口编程
Linux系统内核提供作为网络通信的接口
注:只有应用层是程序员能够自己定义的,下面三层的协议封装都是由内核完成的(但是又选择权力)
1、套接字分类
(1)流式套接字
数据以字节流(二进制流)的方式来进行传递,无大小限制,保证数据可靠,无丢失,顺序发送,主要用于TCP协议,一般情况下只要选择流式方式,那内核就会默认选择TCP传输层协议
(2)数据报套接字
主要通过数据报的方式发送,固定大小。不能保证数据可靠,可能丢失、乱序发送,主要用于UDP协议一般情况下只要选择数据报方式,那内核就会默认选择UDP传输层协议
(3)原始套接字
可以对较低层协议如IP、ICMP直接访问
3、相关接口函数
socket()—创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type,int protocol);
参数:
domain:domain参数指定通信域;这将选择用于通信的协议族。
这些系列被定义在<sys/socket.h>中。
目前被Linux内核理解的格式包括:
AF_UNIX
AF_LOCAL
AF_INET
AF_AX25
AF_IPX
AF_APPLETALK
AF_X25
AF_INET6
AF_DECnet
AF_KEY ....
type:套接字具有指定的类型,该类型指定通信语义。目前定义的类型有:
SOCK_STREAM ---->流式套接字
SOCK_DGRAM ---->数据报套接字
SOCK_RAW ---->原始套接字
protocol:协议指定套接字使用的特定协议。在给定的协议族中,通常只存在“一个协议”来支持特定的套接字类型,在这种情况下,“协议默认为0”。
在多种协议这种情况下,特定的协议必须以这种方式指定。所使用的协议号特定于要在其中进行通信的“通信域”;参见getprotoent(3)了解如何将协议名称字符串映射到协议编号。
bind()—绑定套接字
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
listen()—监听
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
参数:
sockfd:socket返回的监听套接字
backlog:表示同时能够监听的客户个数(不是同时能连接的个数)
返回值:0成功 -1结束
accept()—接收客户端连接请求
后续的数据通信全部使用通信套接字,不能使用监听套接字来通信
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:socket返回的监听套接字
addr: 客户端首地址 如果不需要客户端信息传递-->NULL
addrlen:客户端地址信息长度 如果不需要客户端信息传递-->NULL
返回值:
成功返回用于通信的套接字,错误返回-1
connect()—(客户端)主动发送连接请求
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
read()/write()—数据收发
read------->
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
fd:文件描述名
buf:用户自定义的缓冲区,用于存放准备读入的内容
count:请求读取的字节数
返回值:成功返回读到的字节数,失败返回-1,返回0表示读到文件末尾
----------------------------------------------------------------------------------------------------
write------->
#include <unistd.h>
ssize_t write(int fd, const void *buf,
size_t count);
参数:
fd:文件描述名
buf:用户自定义的缓冲区,用于存放准备写入的内容
count:请求读取的字节数
返回值:成功返回读到的字节数,失败返回-1
close()—关闭套接字
#include <unistd.h>
int close(int fd);//参数填套接字就行,很简单
4、TCP服务器搭建流程
(1)创建套接字----->socket() 正确返回:监听套接字 错误返回:-1
(2)套接字绑定------>bind() 绑定核心:IP地址与PORT端口
(3)监听套接字
(4)建立链接请求
(5)读写
(6)关闭套接字
端口:标识进程 无符号短整型:0~65535 01024被内核使用。用户可指定端口:102465535 IP地址:标识主机 IPV4占四个字节。
形式点分十进制:“192.168.1.82” 给人看的
二进制形式:11000000 10101000 000000010 00000010 给计算机看的
IP地址转换函数
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *)
cp:ip地址的字符串形式
in_addr_t:转换后的二进制地址形式
相关结构体(man 7 ip查询)
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
5、实例:简单地搭建一个TCP服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
perror("socket");
exit(-1);
}
struct sockaddr_in ser;
ser.sin_family=AF_INET;
ser.sin_port=htons(8989);
ser.sin_addr.s_addr=inet_addr("192.168.22.245");
if(-1==bind(sockfd,(struct sockaddr *)&ser,sizeof(ser)))
{
perror("bind");
exit(-1);
}
printf("bind success\n");
if(listen(sockfd,5)==-1)
{
perror("listen");
exit(-1);
}
int connfd=accept(sockfd,NULL,NULL);
while(1)
{
char buf[24]={0};
if(connfd==-1)
{
perror("accept");
exit(-1);
}
read(connfd,buf,24);
printf("%s\n",buf);
printf("accept success\n");
}
close(connfd);
close(sockfd);
return 0;
}
代码运行结果展示:
|