Resumen de programación de red 2

1.TCP

modelo TCP

1. Funciones relacionadas con la construcción de TCP:

 Enchufe

 1) Función del zócalo:

2) enlazar

 3) escucha

 4) aceptar

 5)recepción

 Aviso:

1> recv en TCP se puede reemplazar por lectura; 2> recv en TCP se puede reemplazar por recvfrom

6) enviar

  Aviso:

1> enviar en TCP se puede reemplazar por escritura; 2> enviar en TCP se puede reemplazar por enviar a

7) conectar

 Cliente:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.8.77"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("cfd=%d\n",cfd);
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充地址信息结构体
	//真是的地址信息结构体根据地址族制定  AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填 AF_INET
	sin.sin_port = htons(PORT);//端口号, 1024-49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig
	
	//连接服务器
	if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("connet");
		return -1;
	}
	printf("connect  sucess __%d__\n",__LINE__);

	char buf[128]="";
	ssize_t res=0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		printf("请输入数据>>>");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;

		//发送
		if(send(cfd,buf,sizeof(buf),0)<0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send sucess\n");

		//接收
		bzero(buf,sizeof(buf));
		
		res=recv(cfd,buf,sizeof(buf),0);
		if(res<0)
		{
			ERR_MSG("recv");
			return -1;
		}
		else if(0==res)
		{
			printf("cfd=%d 服务器下线 __%d__\n",	cfd,__LINE__);
			break;
		}
		printf("cfd=%d %s __%d__\n",cfd,buf,__LINE__);

	}

	close(cfd);
	return 0;
}

servidor:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.8.77"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("sfd=%d\n",sfd);
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充地址信息结构体
	//真是的地址信息结构体根据地址族制定  AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填 AF_INET
	sin.sin_port = htons(PORT);//端口号, 1024-49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig

	//将IP和端口号绑定到套接字上
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);

	//阻塞函数,从已完成连接的队列头中获取一个客户端信息
	//该文件描述符才是与客户端通信的文件描述符
	int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
	if(newfd<0)
	{
		ERR_MSG("accept");
		return -1;
	}
	printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

	char buf[128]="";
	ssize_t res=0;
	while(1)
	{
		//接收
	
		res=recv(newfd,buf,sizeof(buf),0);
		if(res<0)
		{
			ERR_MSG("recv");
			return -1;
		}
		else if(0==res)
		{
			printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
			break;
		}
		printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

		//发送
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0)<0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send sucess\n");
	}

	close(newfd);
	close(sfd);
	return 0;
}

2.UDP

modelo UDP

 1. Funciones relacionadas con UDP

1) enchufe

2) enlazar

3)recibido desde

 4) envía dos

 Cliente:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define SER_IP "192.168.8.77" //本机IP 
#define SER_PORT 6666

#define CLI_IP "192.168.8.77" //本机IP 
#define CLI_PORT 8888

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
//*****************************************************	
	struct sockaddr_in cin;
	cin.sin_family  = AF_INET;      //必须填AF_INET
	cin.sin_port = htons(CLI_PORT);     //端口号的网络字节符
	cin.sin_addr.s_addr = inet_addr(CLI_IP);
	
	if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("client bind success\n");
//********************************************************
	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(SER_PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(SER_IP);

	struct sockaddr_in rcvaddr;//存储数据包是从哪里来的
	socklen_t addrlen = sizeof(rcvaddr);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		printf("请输入数据>>");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;

		//发送数据,发送给服务器
		if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("发送数据\n");

		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&rcvaddr,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s : %d]: %s\n",inet_ntoa(rcvaddr.sin_addr), ntohs(rcvaddr.sin_port),buf);
	}
	//关闭文件描述符
	close(cfd);
	
	return 0;
}

servidor:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line%d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.8.77" //本机IP 
#define PORT 6666

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(IP);

	//绑定 必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}

	struct sockaddr_in cin;//存储数据包是从哪里来的
	socklen_t addrlen = sizeof(cin);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s : %d]: %s\n",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),buf);
		
		strcat(buf,"*_*");
		if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,sizeof(cin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("发送成功\n");
	}
	//关闭文件描述符
	close(sfd);
	
	return 0;
}

2. La función de conexión en UDP ( énfasis )

1> La función de conexión en TCP provocará un protocolo de enlace de tres vías para conectar el cliente al servidor; la función de conexión en UDP solo registra la IP y el número de puerto del par en el kernel. En este momento, UDP sólo puede comunicarse con el par registrado.

2>La función de conexión en TCP solo se puede llamar una vez; la función de conexión en UDP se puede llamar varias veces para actualizar la dirección IP y el puerto del par en el kernel. Si desea borrar la información de la dirección del par en el kernel, puede modificar el miembro sin_family a AF_UNSPEC

3>Cuando UDP usa el modo de conexión para enviar y recibir mensajes, puede llamar a la función de envío y escritura o a la función enviar a.

1> enviar a (sd,buf,sizeof (buf), NULL, 0);

recvfrom se puede reemplazar con recv read al completar NULL en la estructura de información de dirección posterior.

2> recvfrom (sockfd, buf, len, banderas, NULL, NULL);

 Las ventajas de que UDP llame a la función de conexión:

1>Mejorar la eficiencia de la transmisión:

a. No llame a Connect: complete el kernel con la información de la dirección del par --> envíe el mensaje ---> borre la información del kernel ---> complete el kernel con la información de la dirección del par --> envíe el mensaje --- > borrar información del kernel

B. Se llama a Connect: complete la información de la dirección del par en el kernel --> enviar mensaje --> enviar mensaje --> enviar mensaje ---> borrar información del kernel

2> Aumentar la estabilidad de la transmisión:

A. La comunicación UDP que llama a la función de conexión puede evitar que el proceso AB reciba mensajes del proceso C durante la transmisión de datos, lo que provoca errores de transmisión.

3. Comunicación multipunto UDP

【1】Atributos de red

conjuntosockopt/getsockopt

 

 [2] Comunicación multipunto

1. unidifusión

1> Modo de comunicación uno a uno entre hosts, conmutadores y enrutadores solo reenvía datos, no los copia

2>Solo dos entidades se comunican entre sí a la vez, y tanto el remitente como el receptor están determinados de forma única.

2. Difusión

1> Modo de comunicación uno a muchos entre hosts, la red copia y reenvía incondicionalmente la señal enviada por cada host

2>Todos los hosts bajo la misma LAN pueden recibir información de transmisión

3> Prohibir que los datos de transmisión pasen a través del enrutador para evitar que los datos de transmisión afecten a los hosts de gran área

4> Los datos de transmisión no requieren respuesta, solo UDP puede transmitir

5>Dirección de transmisión: número de red válido + número de host, todo 1

1) El extremo emisor de la transmisión (similar al cliente)

1> socket crea un socket de informe; no es necesario vincular para vincularse

2> setsockopt establece transmisión permitida: nivel: SOL_SOCKET nombre de opción: SO_BROADCAST

3> Especifique la estructura de información de dirección del extremo receptor

                a.IP: Complete la IP de transmisión

                b.PUERTO: Puede ser consistente con el llenado por el extremo receptor.

4> sendto envía datos de transmisión

2) El extremo receptor de la transmisión (similar al servidor)

1> socket crea un socket de informe; el enlace debe estar vinculado

2> Complete la estructura de información de la dirección del destinatario

  a.IP: Complete la IP de transmisión: número de red válido + número de host, todo 1

                Después de llamar a la función de vinculación, todas las direcciones IP disponibles de la máquina se vincularán al socket

                b. PUERTO: Debe ser el mismo que el completado por el remitente.

3> recvfrom recibe datos de transmisión

3. Multidifusión (grupo de multidifusión)

1) Remitente de multidifusión (similar al cliente)

1>socket crea un zócalo de periódico; la unión no tiene que estar encuadernada

2> Especifique la estructura de información de dirección del extremo receptor

  a.IP: Complete la IP de multidifusión (224.0.0.0~239.255.255.255, que es consistente con el receptor)

                b.PUERTO: Puede ser consistente con el llenado por el extremo receptor.

3> enviar para enviar datos de multidifusión

2) Extremo receptor de multidifusión (similar al servidor)

1> socket crea un socket de informe; el enlace debe estar vinculado

2> setsockopt se une al grupo de multidifusión: nivel: IPPROTO_IP OPTNAME: IP_ADD_MEMBERSHIP   

3> Complete la estructura de información de la dirección del propio destinatario

          a.IP: Complete la IP de multidifusión 

                b. PUERTO: Debe ser el mismo que el completado por el remitente.

4> recvfrom recibe datos de multidifusión

Envío multidifusión:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define GRP_IP "243.1.2.3" //本机IP 
#define PORT 6666

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(GRP_IP);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		printf("请输入数据>>");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;

		//发送数据,发送给服务器
		if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("发送数据\n");
	}
	//关闭文件描述符
	close(cfd);
	return 0;
}

multidifusión:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line%d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.79" //本机IP 
#define GRP_IP "224.1.2.3" //组播IP 
#define PORT 6666

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	//加入多播组
	struct ip_mreqn mq;
	mq.imr_multiaddr.s_addr = inet_addr(GRP_IP);//组播IP
	mq.imr_address.s_addr = inet_addr(IP);
	mq.imr_ifindex=2;
	
	if(setsockopt(sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mq,sizeof(mq))<0)
	{
		perror("setsockopt");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(GRP_IP);

	//绑定 必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	struct sockaddr_in cin;//存储数据包是从哪里来的
	socklen_t addrlen = sizeof(cin);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s : %d]: %s\n",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),buf);
	}
	//关闭文件描述符
	close(sfd);
	return 0;
}

3. Protocolo TFTP

1) Descripción general del protocolo TFTP : el Protocolo simple de transferencia de archivos es un conjunto de protocolos estándar adecuados para la transferencia de archivos en la red, utilizando UDP para la transmisión.

Características: Protocolo de capa de aplicación; implementado según el protocolo UDP

Modo de transferencia de datos:

                     octeto: modo binario (comúnmente usado)

                     correo: ya no es compatible

2) modelo de descarga TFTP

 3) Resumen del proceso de comunicación TFTP

1>El servidor espera la solicitud del cliente en el puerto 69

2> Si el servidor aprueba la solicitud, utilizará el puerto temporal para comunicarse con el cliente.

3> El número de cada paquete cambia (comenzando desde 1)

4> Cada paquete de datos debe ser confirmado por ACK. Si hay un tiempo de espera, debe reenviar el último paquete de datos o paquete ACK.

5>La longitud de los datos se transmite en 512 Bytes, los datos de menos de 512 Bytes significan el final de la transmisión de datos.

 4) análisis del protocolo TFTP

 código:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define SER_IP "192.168.171.1" //本机IP 
#define SER_PORT 69

int do_download(int cfd, struct sockaddr_in sin)
{
	//下载请求
	char filename[20]="";
	char buf[516]={0};
	printf("请输入要下载的文件名>>");
	scanf("%s",filename);
	while(getchar()!=10);

	short*p1 = (short*)buf;
	*p1 = htons(1);
	char*p2 = buf+2;
	strcpy(p2,filename);

	char*p3 = p2+strlen(p2);
	char*p4 = p3+1;
	strcpy(p4,"octet");

	int size=strlen(p2)+strlen(p4)+4;

	//发送下载请求协议
	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	printf("发送成功\n");

	int fd=-1;
	socklen_t  addrlen=sizeof(sin);
	ssize_t res =0;
	unsigned short num=0;
	int ret=0;	
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			ret=-1;
			break;
		}
		
		if(buf[1]==3)
		{
			if(htons(num+1)==*(unsigned short*)(buf+2))
			{
				//组数据包给服务器
				num++;
				if(1==ntohs(*(unsigned short*)(buf+2)))
				{
					fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664);
					if(fd<0)
					{
						ERR_MSG("open");
						return -1;
					}
				}
				if(write(fd,buf+4,res-4)<0)
				{
					ERR_MSG("write");
					ret=-1;
					break;
				}
				//回复ACK,由于ACK包和数据包前四个字节只有操作码不一致
				//直接修改数据包的操作码
				buf[1]=4;
				if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0)
				{
					ERR_MSG("sendto");
					ret =-1;
					break;
				}
				if(res-4<512)
				{
					printf("文件:%s 上传完毕\n",filename);
					break;
				}
			}
		}
		else if(buf[1]==5)//错误包
		{
			fprintf(stderr,"DOWNLOAD_EROR: %d: %s\n",
					ntohs(*(unsigned short*)(buf+2)),buf+4);
			break;
		}
	}
	close(fd);
	return ret;
}

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(SER_PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(SER_IP);

	char c=0;
	while(1)
	{
		system("clear");
		printf("*****************\n");
		printf("******1.下载*****\n");
		printf("******2.上传*****\n");
		printf("******3.退出*****\n");
		printf("*****************\n");
		c=getchar();
		while(getchar()!=10);
		
		switch(c)
		{
		case'1':
			do_download(cfd,sin);
			break;
		case'2':
			break;
		case'3':
			goto END;
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
		printf("请输入任意字符清屏>>>");
		while(getchar()!=10);
	}

END:
	close(cfd);
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/weixin_57039874/article/details/130460061
Recomendado
Clasificación