Directorio de artículos
-
- Comprender TCP, UDP
- Implementar servidor y cliente basados en TCP
-
- Secuencia de llamada de función predeterminada en el lado del servidor TCP
- Ingrese al estado de solicitud de conexión en espera
- Aceptar solicitudes de conexión de clientes
- Orden de llamada de función predeterminado para clientes TCP
- Relación de llamada de función del lado del servidor y del lado del cliente basada en TCP
- Implementar iteraciones del lado del servidor y del lado del cliente.
Comprender TCP, UDP
Pila de protocolo TCP/IP
Pila de protocolos TCP
La pila de protocolos TCP/IP se divide en 4 capas, se puede entender que el envío y la recepción de datos se dividen en 4 procesos jerárquicos.
Pila de protocolos TCP
Pila de protocolos UDP
capa de enlace
La capa de enlace es el resultado de la estandarización en el campo de la conexión física y también es el campo más básico, que define específicamente estándares de red como LAN, WAN y MAN. Dos hosts intercambian datos a través de la red, lo que requiere una conexión física como se muestra en la siguiente figura. La capa de enlace es responsable de estos estándares.
capa IP
El protocolo IP es un protocolo poco fiable orientado a mensajes. Nos ayudará a elegir una ruta cada vez que transmitamos datos, pero no es consistente. Si se produce un error de ruta durante la transmisión, se selecciona otra ruta. Pero si se produce una pérdida de datos o un error, no se puede solucionar. El protocolo IP no puede hacer frente a errores de datos.
Capa TCP/UDP
La capa IP resuelve el problema de la selección de ruta en la transmisión de datos y solo necesita transmitir datos a lo largo de esta ruta. Las capas TCP y UDP completan la transmisión de datos real en función de la información de ruta proporcionada por la capa IP, por lo que esta capa también se denomina capa de transporte. TCP puede garantizar una transmisión de datos confiable, pero envía datos según la capa IP.
TCP y UDP existen por encima de la capa IP y determinan el método de transmisión de datos entre hosts. El protocolo TCP brinda confiabilidad al protocolo IP no confiable después de la confirmación.
Capa de aplicación
Las clases anteriores se manejan automáticamente durante la comunicación por socket. La selección de la ruta de transmisión de datos y el proceso de confirmación de datos están ocultos dentro del socket. Pero sólo dominando estas teorías podremos escribir programas de red que satisfagan nuestras necesidades.
La herramienta proporcionada a todos son los sockets, y todos solo necesitan usar sockets para escribir programas. En el proceso de escritura de software, es necesario determinar las reglas de transmisión de datos entre el servidor y el cliente en función de las características del programa, que es el protocolo de la capa de aplicación. Una gran parte de la programación de redes consiste en diseñar e implementar protocolos de capa de aplicación.
Implementar servidor y cliente basados en TCP
Secuencia de llamada de función predeterminada en el lado del servidor TCP
1、socket() 创建套接字
2、bind() 分配套接字地址
3、listen() 等待连接请求状态
4、accept() 允许连接
5、read()/write() 数据交换
6、close() 断开连接
Ingrese al estado de solicitud de conexión en espera
Hemos llamado a la función de enlace para asignar una dirección al socket. A continuación, llamamos a la función de escucha para ingresar al estado de espera de una solicitud de conexión. Solo llamando a la función de escucha puede el cliente ingresar a un estado en el que pueda emitir una conexión. solicitud. En este momento, el cliente puede llamar a la función connect.
#include<sys/socket.h>
int listen(int sock, int backlog);
成功返回0,失败返回-1
sock 为希望进入等待连接请求状态的文件描述符,传递的描述符套接字参数为服务器端套接字
backlog 连接请求等待队列的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列
El estado de solicitud de conexión en espera significa que cuando el cliente solicita una conexión, la conexión se mantiene en estado de espera antes de aceptar la conexión. La solicitud de conexión del cliente en sí también es un tipo de datos recibidos en la red y se requiere un socket para aceptar. él.
Aceptar solicitudes de conexión de clientes
Después de llamar a la función de escucha, si hay nuevas solicitudes de conexión, deben aceptarse en orden. Aceptar una solicitud significa ingresar a un estado en el que se pueden aceptar datos. En este momento, se necesita un socket para aceptar datos, pero el socket del lado del servidor actúa como un guardián y ya no puede desempeñar la función de aceptar datos. Por lo tanto, se necesita otro socket. No es necesario crear este socket personalmente. La función de aceptación creará el socket y se conectará con el cliente que inició la solicitud.
#include <sys/socket.h>
int accept(int sock, struct sockaddr* addr, socklen_t* addrlen);
成功返回创建的套接字文件描述符,失败返回-1
sock 服务器套接字的文件描述符
addr 保存发起连接请求的客户端地址信息的变量的地址,调用函数后会向该变量填充客户端地址信息
addrlen 第二个参数addr结构体的长度,调用函数后会向该变量填充客户端地址长度
La función de aceptación acepta solicitudes de conexión y espera solicitudes de conexión de clientes pendientes en la cola. Cuando la llamada a la función es exitosa, la función de aceptación generará internamente un socket para E/S de datos y devolverá el descriptor del archivo. El socket se crea automáticamente y se establece automáticamente una conexión con el cliente que inició la solicitud de conexión.
Orden de llamada de función predeterminado para clientes TCP
Secuencia de llamada de funciones de cliente TCP
1、socket() 创建套接字
2、connect() 请求连接
3、read()/write() 交换数据
4、closr() 断开连接
En comparación con el lado del servidor, la diferencia radica en la solicitud de conexión, que hace que la solicitud de conexión se inicie en el lado del servidor después de crear el socket del cliente. El lado del servidor llama a la función de escucha y crea una cola de espera de solicitudes, y luego el cliente puede solicitar una conexión.
#include<sys/socket.h>
int connect(int sock, struct sockaddr* servaddr, socklen_t addrlen);
成功返回0,失败返回-1
sock 客户端套接字文件描述符
servaddr 保存目标服务器地址信息的变量地址值
addrlen 以字节为单位,传递第二个参数的地址变量的长度
Después de que el cliente llame a la función de conexión, regresará solo si ocurren las siguientes situaciones
- El servidor acepta la solicitud de conexión.
- La solicitud de conexión se interrumpe debido a circunstancias anormales, como la desconexión de la red.
Aceptar una solicitud de conexión no significa que el servidor llame a la función de aceptación. De hecho, el servidor registra la información de la solicitud de conexión en la cola de espera, por lo que el intercambio de datos no se realiza inmediatamente después de que regresa la función de conexión.
Relación de llamada de función del lado del servidor y del lado del cliente basada en TCP
El proceso general es el siguiente:
Después de que el servidor crea el socket, llama continuamente a las funciones de enlace y escucha para entrar en el estado de espera. El cliente inicia una solicitud de conexión llamando a la función de conexión. El cliente espera hasta que el servidor llama a la función de escucha antes de llamar a conectar para iniciar una conexión. solicitud. El cliente también debe ser independiente. Antes de llamar a la función de conexión, el servidor puede llamar primero a la función de aceptación. En este momento, el servidor llama a la función de aceptación y entra en el estado de bloqueo hasta que el cliente llama a la función de conexión.
Implementar iteraciones del lado del servidor y del lado del cliente.
Implementar la iteración del lado del servidor.
La forma más sencilla de implementar la iteración en el lado del servidor es insertar una declaración de bucle y llamar a la función de aceptación repetidamente. El cierre (cliente) al final del ciclo cierra el socket creado al llamar a la función de aceptación, lo que significa que el servicio para un determinado cliente ha finalizado. Si aún desea atender a otros clientes en este momento, debe llamar a aceptar funcionar nuevamente. Actualmente, solo puede atender a un cliente al mismo tiempo. Después de aprender los procesos y subprocesos, puede escribir un servidor que atienda a varios clientes al mismo tiempo.
Lado del servidor de eco iterativo, lado del cliente
El modo de funcionamiento básico del servidor echo y el cliente echo compatible:
- El servidor solo está conectado a un cliente al mismo tiempo y proporciona servicios de eco.
- El servidor proporciona servicios a 5 clientes por turno y sale.
- El cliente acepta la cadena de entrada del usuario y la envía al servidor.
- El servidor transmite los datos de la cadena recibida al cliente, es decir, "eco".
- El eco de la cadena entre los dos extremos se realiza hasta que el cliente ingresa Q.
Primero, presentamos el servidor echo que cumple con los requisitos anteriores.
servidor_echo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
char message[BUF_SIZE];
int str_len, i;
struct sockaddr_in serv_adr;
struct sockaddr_in clnt_adr;
socklen_t clnt_adr_sz;
if(argc!=2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock==-1)
error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
clnt_adr_sz=sizeof(clnt_adr);
for(i=0; i<5; i++)
{
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
if(clnt_sock==-1)
error_handling("accept() error");
else
printf("Connected client %d \n", i+1);
while((str_len=read(clnt_sock, message, BUF_SIZE))!=0)
write(clnt_sock, message, str_len);
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
resultado de la operación
gcc echo_server.c -o eserver
./eserver 9190
输出:
Connecten client 1
Connecten client 2
Connecten client 3
código de cliente de eco
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_adr;
if(argc!=3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock=socket(PF_INET, SOCK_STREAM, 0);
if(sock==-1)
error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
serv_adr.sin_port=htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
error_handling("connect() error!");
else
puts("Connected...........");
while(1)
{
fputs("Input message(Q to quit): ", stdout); //如果输入Q说明结束while循环
fgets(message, BUF_SIZE, stdin);
if(!strcmp(message,"q\n") || !strcmp(message,"Q\n")) //检验message是否为Q/q
break;
write(sock, message, strlen(message));
str_len=read(sock, message, BUF_SIZE-1);
message[str_len]=0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
resultado de la operación
gcc echo_client.c -o eclient
./eclient 192.168.233.20 9190
输出:
Connected ....
Input message: hello
Message from server: hello
Input message : Q
Este es el cuarto artículo de la columna "Programación de redes TCP/IP". ¡Los lectores pueden suscribirse!
Para obtener más información, haga clic en GitHub Bienvenidos lectores a Star
⭐Grupo de intercambio académico Q 754410389 se está actualizando~~~