其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
所谓【套接字】(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口
第一阶段:ARPAnet(阿帕网),是网络基础协议得雏形
第二阶段:第一份IP协议说明书
第三阶段:TCP/IP协议被制定
<1>C/S:表示客户端/服务器设计框架
<2>B/S:表示浏览器/服务器设计框架
OSI分层模型:理想的网络分层模型
TCP/IP分层模型:一种标准的网络分层模型
1.应用层:可以由用户自定义的协议,方便数据传输以及数据加密
2.传输层:提供端对端的数据传输
3.网络层:提供数据的路由
4.网络接口层:实现网络底层驱动
Linux系统内核提供作为网络通信的接口
注:只有应用层是程序员能够自己定义的,下面三层的协议封装都是由内核完成的(但是又选择权力)
数据以字节流(二进制流)的方式来进行传递,无大小限制,保证数据可靠,无丢失,顺序发送,主要用于TCP协议,一般情况下只要选择流式方式,那内核就会默认选择TCP传输层协议
主要通过数据报的方式发送,固定大小。不能保证数据可靠,可能丢失、乱序发送,主要用于UDP协议一般情况下只要选择数据报方式,那内核就会默认选择UDP传输层协议
可以对较低层协议如IP、ICMP直接访问
#include
#include
int socket(int domain, int type,int protocol);
参数:
domain:domain参数指定通信域;这将选择用于通信的协议族。
这些系列被定义在中。
目前被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)了解如何将协议名称字符串映射到协议编号。
#include /* See NOTES */
#include
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
#include /* See NOTES */
#include
int listen(int sockfd, int backlog);
参数:
sockfd:socket返回的监听套接字
backlog:表示同时能够监听的客户个数(不是同时能连接的个数)
返回值:0成功 -1结束
后续的数据通信全部使用通信套接字,不能使用监听套接字来通信
#include /* See NOTES */
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:socket返回的监听套接字
addr: 客户端首地址 如果不需要客户端信息传递-->NULL
addrlen:客户端地址信息长度 如果不需要客户端信息传递-->NULL
返回值:
成功返回用于通信的套接字,错误返回-1
#include /* See NOTES */
#include
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
read------->
#include
ssize_t read(int fd, void *buf, size_t count);
参数:
fd:文件描述名
buf:用户自定义的缓冲区,用于存放准备读入的内容
count:请求读取的字节数
返回值:成功返回读到的字节数,失败返回-1,返回0表示读到文件末尾
----------------------------------------------------------------------------------------------------
write------->
#include
ssize_t write(int fd, const void *buf,
size_t count);
参数:
fd:文件描述名
buf:用户自定义的缓冲区,用于存放准备写入的内容
count:请求读取的字节数
返回值:成功返回读到的字节数,失败返回-1
#include
int close(int fd);//参数填套接字就行,很简单
(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
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字,第一个参数为:协议族,第二个参数:套接字类型,第三个参数:0代表只存在一个协议来支持套接字类型
if(sockfd==-1)//错误返回
{
perror("socket");
exit(-1);
}
struct sockaddr_in ser;//定义ip地址转化的相关结构体
ser.sin_family=AF_INET;//初始化族
ser.sin_port=htons(8989);//初始化端口---1024~65535都可以
ser.sin_addr.s_addr=inet_addr("192.168.22.245");//初始化ip地址
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;
}