一、ISO/OSI参考模型
OSI(open system interconnection)开放系统互联模型是由ISO(International Organization for Standardization)国际标准化组织定义的网络分层模型,共七层
http协议、smtp pop协议、ftp协议、fix协议、ssh协议
物理层(Physical Layer):物理层定义了所有电子及物理设备的规范,为上层的传输提供了一个物理介质,本层中数据传输的单位为比特(bit)。属于本层定义的规范有EIA/TIA RS-232、EIA/TIA RS-449、V.35、RJ-45等,实际使用中的设备如网卡等属于本层。
数据链路层(Data Link Layer):对物理层收到的比特流进行数据成帧。提供可靠的数据传输服务,实现无差错数据传输。在数据链路层中数据的单位为帧(frame)。属于本层定义的规范有SDLC、HDLC、PPP、STP、帧中继等,实际使用中的设备如switch交换机属于本层。
网络层(Network Layer):网络层负责将各个子网之间的数据进行路由选择,分组与重组。本层中数据传输的单位为数据包(packet)。属于本层定义的规范有IP、IPX、RIP、OSPF、ICMP、IGMP等。实际使用中的设备如路由器属于本层。
传输层(Transport Layer):提供可靠的数据传输服务,它检测路由器丢弃的包,然后产生一个重传请求,能够将乱序收到的数据包重新排序。
会话层(Session Layer):管理主机之间会话过程,包括会话建立、终止和会话过程中的管理。
表示层(Presentation Layer):表示层对网络传输的数据进行变换,使得多个主机之间传送的信息能够互相理解,包括数据的压缩、加密、格式转换等。
应用层(Application Layer):应用层与应用程序界面沟通,以达至展示给用户的目的。 在此常见的协定有: HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等
二、TCP/IP四层模型
三、端口
不同应用开放不同端口,一个网站默认端口80,ip+端口号 确定服务。
TCP特点:
1.基本于字节流
2.面向连接
3.可靠传输
4.缓冲传输
5.全双工
6.流量控制
TCP如何保证可靠性:
应用数据被分割成TCP认为最适合发送的数据块,称为段传递给IP层。
当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
TCP将保持它首部和数据的校验和。这是一个端到端的校验和,目的是检测数据在传输过程中的任何变化。如果收到段的校验和有差错,TCP将丢弃这个报文段并且不确认(导致对方超时重传)
TCP承载于IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对收到的数据进行重新排序。
IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
TCP还能提供流量控制。TCP连接的每一方都有一定大小的缓冲空间。
socket编程
一、什么是socket编程
进程间通信,只不过不在同一台机器。
socket可以看成是用户进程与内核网络协议栈的编程接口。
socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信
tcp_client 配合TCP-UDP服务管理 V1.03
#include <stdio.h>
#include<unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define SERVER_IP "192.168.0.108"
#define SERVER_PORT 6800
int main()
{
int ret = 0;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket error");
goto err;
}
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(SERVER_PORT);
sockaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
ret = connect(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr));
if(ret < 0){
perror("connect error");
goto err;
}
char buf[256] = {0};
scanf("%s",buf);
write(sock, buf, sizeof(buf));
read(sock, buf, sizeof(buf));
printf("buf %s\n", buf);
if(sock > 0){
close(sock);
}
return 0;
err:
if(sock > 0){
close(sock);
}
return -1;
}
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <pthread.h>
void *thread_new_client(void *para)
{
char buf[128]={0};
int cnfd = *(int *)para;
while(1)
{
if(-1 == read(cnfd,buf,sizeof(buf))){
perror("read error:");
return 0;
}
//start buf end
printf("server read from client:%s\r\n",buf);
if(strcmp(buf, "end") == 0)
break;
memset(buf, 0, sizeof(buf));
strcpy(buf, "hello , i am server");
if(-1 == write(cnfd,buf,strlen(buf))){
perror("Send error:");
return 0;
}
}
close(cnfd);
return 0;
}
/*
INADDR_ANY就是指定地址为0.0.0.0的地址,
这个地址事实上表示不确定地址,或“所有地址”、
“任意地址”。 一般来说,在各个系统中均定义成为0值
一般情况下,如果你要建立网络服务器应用程序,
则你要通知服务器操作系统:请在某地址 xxx.xxx.xxx.xxx上
的某端口 yyyy上进行侦听,并且把侦听到的数据包发送给我。
这个过程,你是通过bind()系统调用完成的。——也就是说,
你的程序要绑定服务器的某地址,或者说:把服务器的某地址
上的某端口占为已用。服务器操作系统可以给你这个指定的
地址,也可以不给你。
如果你的服务器有多个网卡(每个网卡上有不同的IP地址),
而你的服务(不管是在udp端口上侦听,还是在tcp端口上侦听),
出于某种原因:可能是你的服务器操作系统可能随时增减IP地址,
也有可能是为了省去确定服务器上有什么网络端口(网卡)的
麻烦 —— 可以要在调用bind()的时候,告诉操作系统:“
我需要在 yyyy 端口上侦听,所有发送到服务器的这个端口,
不管是哪个网卡/哪个IP地址接收到的数据,都是我处理的。”
这时候,服务器程序则在0.0.0.0这个地址上进行侦听
*/
void *thread_accept(void *para)
{
pthread_t new_client[5];
int skfd,cnfd,addr_len,client_ix = 0;
struct sockaddr_in srv_addr,clt_addr;
int portnumber = *(int *)para;
/* 创建IPv4的流式套接字描述符 */
if(-1 == (skfd=socket(AF_INET,SOCK_STREAM,0))) //skfd是监听SOCKET
{
perror("Socket Error:");
exit(1);
}
/* 填充服务器端sockaddr地址结构 */
bzero(&srv_addr,sizeof(struct sockaddr_in));
srv_addr.sin_family=AF_INET;
//srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
srv_addr.sin_addr.s_addr=inet_addr("192.168.146.136");
srv_addr.sin_port=htons(portnumber);
/* 将套接字描述符skfd和地址信息结构体绑定起来 */
if(-1 == bind(skfd,(struct sockaddr *)(&srv_addr),sizeof(struct sockaddr)))
{
perror("Bind error:");
return 0;
}
/* 将skfd转换为被动监听模式 */
if(-1 == listen(skfd,4))
{
perror("Listen error:");
exit(1);
}
while(1)
{
printf("accept to wait client to connect\n");
/* 调用accept,服务器端一直阻塞,直到客户程序与其建立连接成功为止*/
addr_len=sizeof(struct sockaddr_in);
if(-1 == (cnfd=accept(skfd,(struct sockaddr *)(&clt_addr),&addr_len)))
//cnfd是新连接上来的客户端的链路标识符
{
perror("Accept error:");
exit(1);
}
printf("Connect from %s:%u ...!\n",inet_ntoa(clt_addr.sin_addr),ntohs(clt_addr.sin_port));
if(client_ix < 5)
pthread_create( &new_client[client_ix++], NULL, thread_new_client, &cnfd);
}
close(skfd);
return 0;
}
int main(int argc, char *argv[])//./tcp_server 1234
{
int portnumber;
pthread_t th_accept_id;
if(2 != argc || 0 > (portnumber=atoi(argv[1])))
{
printf("Usage:%s port\n",argv[0]);
exit(1);
}
pthread_create( &th_accept_id, NULL, thread_accept, &portnumber);
pthread_join(th_accept_id, NULL);
exit(0);
}
tcp_client端
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])//./tcp_client 127.0.0.1 1234
{
int skfd;
char buf[128] = {0};
struct sockaddr_in server_addr;
struct hostent *host;
int portnumber,nbytes;
if(3 != argc || 0>(portnumber=atoi(argv[2])))
{
printf("Usage:%s hostname portnumber \n", argv[0]);
exit(1);
}
if(NULL == (host=gethostbyname(argv[1])))
{
perror("Gethostname error:");
exit(1);
}
/* 创建socket描述符 */
if(-1 == (skfd=socket(AF_INET,SOCK_STREAM,0)))
{
perror("Socket Error:");
exit(1);
}
/* 客户端填充需要连接的服务器的地址信息结构体 */
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);
/* 客户端调用connect主动发起连接请求 */
if(-1 == connect(skfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)))
{
perror("Connect Error:");
exit(1);
}
while(1)
{
printf("please input msg to server:");
scanf("%s",buf);
if(-1 == write(skfd,buf,strlen(buf)+1)){
perror("Send error:");
exit(1);
}
if(strcmp(buf, "end") == 0)
break;
memset(buf, 0, sizeof(buf));
if(-1 == read(skfd,buf,1024)){
perror("read Error:");
}
printf("client read from server:%s\r\n",buf);
}
/* 拆除TCP连接 */
close(skfd);
exit(0);
}
二、UDP
一种标准的类比是:使用无连接协议UDP就像寄信,而使用面向连接的协议TCP就像打电话。我们可以把 TCP 连接中的网络地址当作一个办公室总机的电话号码,把端口号当作办公室中某台正被呼叫的特定电话的分机号。同理,可以将UDP网络地址当作一座公寓楼的地址,并把端口号当作公寓楼大厅中的个人邮箱。
与TCP C/S通信的区别在于:服务端没有设置监听和等待连接的过程。客户端没有连接服务端的过程。基于UDP的通信是不可靠地,面向无连接的,发送的数据无法确切知道对方收到没有,就算对方根本不存在,也可以发送数据出去。这样的通信通常用在对可靠性、安全性要求不高的地方,比如语音通信(没听清楚可以让对方再说一遍)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
信号量