3,linux网络编程
三元组(ip地址,协议,端口)就可以标识网络的进程
3.1,OSI七层模型和TCP/IP五层模型
OSI七层网络模型由下至上为1至7层,分别为:
物理层(Physical layer),数据链路层(Data link layer),网络层(Network layer),传输层(Transport layer),会话层(Session layer),表示层(Presentation layer),应用层(Application layer)。
1.1 应用层,很简单,就是应用程序。这一层负责确定通信对象,并确保由足够的资源用于通信,这些当然都是想要通信的应用程序干的事情。
1.2 表示层,负责数据的编码、转化,确保应用层的正常工作。这一层,是将我们看到的界面与二进制间互相转化的地方,就是我们的语言与机器语言间的转化。数据的压缩、解压,加密、解密都发生在这一层。这一层根据不同的应用目的将数据处理为不同的格式,表现出来就是我们看到的各种各样的文件扩展名。
1.3 会话层,负责建立、维护、控制会话,区分不同的会话,以及提供单工(Simplex)、半双工(Half duplex)、全双工(Full duplex)三种通信模式的服务。我们平时所知的NFS,RPC,X Windows等都工作在这一层。
1.4 传输层,负责分割、组合数据,实现端到端的逻辑连接。数据在上三层是整体的,到了这一层开始被分割,这一层分割后的数据被称为段(Segment)。三次握手(Three-way handshake),面向连接(Connection-Oriented)或非面向连接(Connectionless-Oriented)的服务,流控(Flow control)等都发生在这一层。
1.5 网络层,负责管理网络地址,定位设备,决定路由。我们所熟知的IP地址和路由器就是工作在这一层。上层的数据段在这一层被分割,封装后叫做包(Packet),包有两种,一种叫做用户数据包(Data packets),是上层传下来的用户数据;另一种叫路由更新包(Route update packets),是直接由路由器发出来的,用来和其他路由器进行路由信息的交换。
1.6 数据链路层,负责准备物理传输,CRC校验,错误通知,网络拓扑,流控等。我们所熟知的MAC地址和交换机都工作在这一层。上层传下来的包在这一层被分割封装后叫做帧(Frame)。
1.7 物理层,就是实实在在的物理链路,负责将数据以比特流的方式发送、接收,就不多说了。
3.2,TCP握手
3.2.1 三次握手建立连接
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
1,客户端向服务器发送一个SYN J
2,服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
3,客户端再想服务器发一个确认ACK K+1
从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
三次握手的目的是建立双向的连接,第一次握手是客户端向服务器端发出请求
第二次握手是服务器端告诉客户端,第一次握手是成功的,即可以从客户端发送到客户端,
第三次握手是客户端告诉服务器端,第二次握手是成功的,即可以从客户端到服务器端
这样就保证了客户端和服务器端的双向通信,
3.2.2 四次握手释放连接
1,某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
2,另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
3,一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
4,接收到这个FIN的源发送端TCP对它进行确认。
3.3,socket编程
http://www.cnblogs.com/goodcandle/archive/2005/12/10/socket.html
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
3.3.1 AF_INET TCP传输最简单版本
tcp_client.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 6666
#define BUFF_SIZE 1024
#define MAXLEN 4096
main(int argc, char** argv)
{
if(argc!=2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
char sendline[4096];
//socket()建立socket
int sockfd = socket(AF_INET,SOCK_STREAM,0);
//要连接的服务器地址
struct sockaddr_in sliaddr;
sliaddr.sin_family = AF_INET;
sliaddr.sin_port = htons(PORT);
inet_pton(AF_INET, argv[1], &sliaddr.sin_addr);
//connect()发送请求(ip=argv[1],protocal=TCP,port=6666)
connect(sockfd, (struct sockaddr*)&sliaddr, sizeof(sliaddr));
//recv sth
recv_len = recv(sockfd, buff, sizeof(buff), 0);
buff[recv_len] = '\0';
printf(" %s ", buff);
//interactive
while (1)
{
printf("Enter string to send: ");
scanf("%s", buff);
if (!strcmp(buff, "quit"))
break;
send_len = send(sockfd, buff, strlen(buff), 0);
recv_len = recv(sockfd, buff, BUFF_SIZE, 0);
buff[recv_len] = '\0';
printf(" received: %s \n", buff);
}
//close()关闭连接
close(sockfd);
}
tcp_server.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 6666
#define WAIT_QUEUE_LEN 5
#define BUFF_SIZE 1024
#define WELCOME "Welcome to my server ^_^!\n"
main()
{
int connfd;
char buff[MAXLEN];
int len;
//socket() 建立socket,其中SOCK_STREAM表示tcp连接
int sockfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
//bind()绑定一个socket(ip=all,protocal=TCP,port=6666)
bind(sockfd,(struct sockaddr *) &servaddr, sizeof(servaddr));
//listen()监听
listen(sockfd, WAIT_QUEUE_LEN);
//accept() & close()
printf("======waiting for client's request======\n");
while(1){
c_addrlen = sizeof(struct sockaddr_in);
connfd = accept(serverfd, (struct sockaddr *)&caddr, &c_addrlen);
printf("client: ip=%s,port=%s\n", cliaddr.sin_addr.s_addr,cliaddr.sin_port);
//send a welcome
send(connfd, WELCOME, strlen(WELCOME), 0);
//阻塞模式下recv==0表示客户端已断开连接
while ((len = recv(connfd, buff, BUFF_SIZE, 0)) > 0)
{
buff[len] = '\0';
printf("recv msg is : %s \n", buff);
send(connfd, buff, len, 0);
}
close(connfd);
}
//close()关闭连接
close(sockfd);
}
阻塞与非阻塞recv返回值没有区分,都是
<0 出错
=0 连接关闭
>0 接收到数据大小,
makefile
.PHONY : main
main : server client
server : server.o
gcc -g -o server server.o
client : client.o
gcc -g -o client client.o
server.o : server.c
gcc -g -c server.c
client.o : client.c
gcc -g -c client.c
clean :
rm -rf *.o
ser :
./server
cli :
./client