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