在进socket的了解过程之中我们先了解一下:
1.网络:把多台主机连接起来就构成了网络
2.互联网:网络和网络连接起来就是互联网
3.IP:在网络中唯一标识一个主机
IP地址由两部分组成网络号和主机号
4.MAC地址物理地址 标识一台主机(类似于在学校学号可以标识你 学生姓名也可以标识你)
5.端口号short为了在主机上标识一个进程或者应用程序
6.网络分层分成OSI模型和TCP/IP模型
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
应用层 传输层 网络层 数据链路层
为什么要分层?
各层要独立 灵活性好
数据链路层的协议就是ARP和RARP协议实现了IP地址和物理地址之间的转换
数据链路层解决的是相邻之间的地址
不相邻就是网络层的任务啦
大端字节序是指高位字节存储在低地址,低位字节放在高地址和小端字节序是高放高低放低,我们生活中都属于大端字节序,而计算机能识别的是小端字节序。因此在两个不同字节序的主机进行交流的时候,必须把使用大端的字节序转化成小端字节序。
在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
上面我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。
对于服务器端 ,套接字的创立包括socke()函数、bind()函数、listen()监听 、等待客户端连接,通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
SOCKET的三次握手是指
我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:
客户端向服务器发送一个SYN J
服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
客户端再想服务器发一个确认ACK K+1
当客户端调用connect()向服务器端发送SYN J 数据包此时connect阻塞 服务器端被动打开,Accept(),服务器接收到连接请求向客户端发送 ACK J+1并且SYN,这个时候accept陷入阻塞客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。
代码:
ser.c
#include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<assert.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<netinet/in.h>
9
10 int main()
11 {
12 int sockfd=socket(AF_INET,SOCK_STREAM,0);
13 assert(sockfd!=-1);
14
15 struct sockaddr_in saddr ;
16 memset(&saddr,0,sizeof(saddr));
17 saddr.sin_family=AF_INET;
18 saddr.sin_port = htons(6000);
19 saddr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
20
21 int res =connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
22
23 assert(res!=-1);
26 while(1)
27 {
28 printf(“input\n”);
29 char buff[128]={0};
30
31 fgets(buff,128,stdin);
32
33 if(strncmp(buff,“end”,3)==0)
34 {
35 break;
36 }
37
38 send(sockfd,buff,strlen(buff),0);
39
40
41 memset(buff,0,128);
42 recv(sockfd,buff,127,0);
43 printf(“buff=%s\n”,buff);
44 }
45
46 close(sockfd);
47 exit(0);
cli.c
#include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<assert.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<netinet/in.h>
9
10 int main()
11 {
12 int sockfd=socket(AF_INET,SOCK_STREAM,0);
13 assert(sockfd!=-1);
14
15 struct sockaddr_in saddr ;
16 memset(&saddr,0,sizeof(saddr));
17 saddr.sin_family=AF_INET;
18 saddr.sin_port = htons(6000);
19 saddr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
20
21 int res =connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
22
23 assert(res!=-1);
while(1)
27 {
28 printf(“input\n”);
29 char buff[128]={0};
30
31 fgets(buff,128,stdin);
32
33 if(strncmp(buff,“end”,3)==0)
34 {
35 break;
36 }
37
38 send(sockfd,buff,strlen(buff),0);
39
40
41 memset(buff,0,128);
42 recv(sockfd,buff,127,0);
43 printf(“buff=%s\n”,buff);
44 }
45
46 close(sockfd);
47 exit(0);