TCP/UDP

一、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;
}

tcp_server
#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> 



信号量



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值