Explicación detallada de la programación TCP

1.1 Introducción TCP, proceso de programación

Revisión de TCP:
1. Se debe proporcionar un protocolo de transmisión de datos confiable, orientado a la conexión y una confirmación correspondiente cada vez que se reciben datos; 2. Se debe establecer una conexión
antes de la comunicación;
3. El servidor es un conexión pasiva, el cliente es un enlace activo

La diferencia entre TCP y UDP
:

Arquitectura TCP C/S

Proceso de programación TCP

Servidor:
​ Crea un socket socket()
​ Vincula el socket a la estructura de información de la red del servidor bind()
​ Establece el socket en el estado de escucha listen()
​ Bloquea y espera la solicitud de conexión del cliente accept() (función de bloqueo)
para comunique recv()/send()
para cerrar el socket close()

Cliente:
Crear socket socket()
Enviar solicitud de conexión de cliente connect()
Comunicar send()/recv()
Cerrar socket close()

1.2 Programación TCP—socket
#include <sys/types>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
功能:创建一个套接字,返回一个文件描述符
参数:
	domain:	通信域,协议族
			AF_UNIX		本地通信
			AF_INET		ipv4网络协议
			AF_INET6	ipv6网络协议
			AF_PACKET	底层接口
	type:	套接字的类型
			SOCK_STREAM		流式套接字(tcp)
			SOCK_DGRAM		数据报套接字(udp)
			SOCK_RAW		原始套接字
	protocol:	附加协议,如果不需要,则设置为0
返回值:
	成功:	文件描述符
	失败:	-1

caso:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>

int main(int argc, char **argv){
    
    
	//通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		perror("fail to socket");
		exit(1);
	}
	return 0;
}
1.3 conectar, enviar, recibir
1.3.1 función de conexión
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr* addr, socklen_t len);
功能:	给服务器发送客户端的连接请求
参数:	
	 sockfd:	文件描述符,socket函数的返回值
	 addr:		要连接的服务器的网络信息结构以(需要自己设置)
	 addrlen:	addr的长度
返回值:
	 成功:0
	 失败:-1

Nota:
1. Connect no generará un nuevo socket después de que se establezca la conexión
2. Los datos TCP solo se pueden transmitir después de que la conexión sea exitosa
3. Archivo de encabezado: #include<sys/socket.h>

1.3.2 función de envío
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd,const void *buf,size_t len,int flags);
功能:		发送数据
参数:	
	sockfd:	文件描述符
			客户端:socket函数的返回值
			服务器:accept函数的返回值
	buf:	发送的数据
	len:	buf的长度
	flags:	标志位
			0	阻塞
			MSG_DONTWAIT	非阻塞
返回值:
	成功:发送的字节数
	失败:-1

Nota:
no puede enviar paquetes de datos de longitud 0 con el protocolo TCP

1.3.3 función de recepción
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd,void *buf,size_t len,int flags);
功能:		接收数据
参数:	
	sockfd:	文件描述符
			客户端:socket函数的返回值
			服务器:accept函数的返回值
	buf:	发送的数据
	len:	buf的长度
	flags:	标志位
			0	阻塞
			MSG_DONTWAIT	非阻塞
返回值:
	成功:接收的字节数
	失败:-1
	如果发送端关闭文件描述符或者关闭进程,则recv函数会返回0
1.3.4 Código de cliente

Use el asistente de depuración de red en Windows como servidor

Código del cliente:

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

int main(int argc, char **argv){
    
    

	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		perror("fail to socket");
		exit(1);
	}
	
	//第二步:发送客户端连接请求
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(agrv[2]));
	
	if(connect(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		perror("fail to connect");
		exit(1);
	}
	
	//第三步:进行通信
	//发送数据
	char buf[128] = "";
	fgets(buf,128,stdin);
	buf[strlen(buf) - 1] = '\0';
	if(send(sockfd,buf,128,0) == -1){
    
    
		perror("fail to send");
		exit(1);
	}
	//接收数据
	char text[128] = "";
    if(recv(sockfd,text,128,0) == -1){
    
    
    	perror("fail to recv");
    	exit(1);
    }
    
    printf("from server: %s\n",text);
    
    //第四步:关闭套接字文件描述符
    close(sockfd);
	
	return 0;
}
1.4 Servidor TCP: enlazar, escuchar, aceptar
1.4.1 Condiciones requeridas como servidor TCP

1. Tener una dirección conocida
2. Informar al sistema operativo que es un servidor, no un cliente
3. Esperar a que se establezca la conexión

Para el protocolo TCP orientado a conexión, el establecimiento de una conexión realmente significa el comienzo de la comunicación de datos.

1.4.2 función de enlace
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
功能:将套接字与网络信息结构体绑定
参数:
	sockfd:文件描述符,socket的返回值
	addr:	网络信息结构体  //#include <netinet/in.h>
	addrlen:addrlen的长度
返回值:
	成功: 0
	失败: -1
1.4.3 función de escucha
#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);
功能:
	将套接字由主动修改为被动
	使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接
参数:
	sockfd:	socket监听套接字
	backlog:连接队列的长度
返回值:
	成功:返回0
	失败:其他
1.4.4 función aceptar
#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
功能:
	从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)
参数:
	sockfd:	socket监听套接字
	cliaddr:用于存放客户端套接字地址结构
	addrlen:套接字地址结构体长度的地址
返回值:
	成功:新的文件描述符(只要由客户端连接,就会产生新的文件描述符,这个新的文件描述符专门与指定的客户端进行通信的)
	失败:-1
	
注意:返回的是一个已连接套接字,这个套接字代表当前这个连接
1.4.5 Ejemplo de servidor TCP
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
    
    
	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		perror("fail to socket");
		exit(1);
	}
	
	//第二步:将套接字与服务器网络信息结构体绑定
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		perror("fail to bind");
		exit(1);
	}
	
	//第三步:将套接字设置为被动监听状态
	if(listern(sockfd,10) == -1){
    
    
		perror("fail to listen");
		exit(1);
	}
	
	//第四步:阻塞等待客户端的连接请求
	int acceptfd;
	struct sockaddr_in clientaddr;
	if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen)) == -1){
    
    
		perror("fail to accept");
		exit(1);
	}
	//打印连接的客户端信息
	printf("ip:%s ,port:%d\n",inet_ntoa(clientaddr),ntohs(clientaddr.sin_port));
	
	//第五步:进行通信
	char buf[128] = "";
	if(recv(acceptfd,buf,128,0) == -1){
    
    
		perror("fail to recv");
		exit(1);
	}
	
	printf("from client: %s\n",buf);
	
	strcat(buf,"*_*");
	if(send(acceptfd,buf,128,0) == -1){
    
    
		perror("fail to send");
		exit(1);
	}
	
	return 0;
}
1.5 Programación TCP: cierre, protocolo de enlace de tres vías, onda de cuatro vías
1.5.1 cerrar cierra el zócalo

1. Utilice la función de cierre para cerrar el zócalo (cerrar un zócalo conectado hará que el otro extremo reciba un paquete de datos de longitud 0)

2. Cuando se trabaja como servidor
1> Cerrar el socket de escucha hará que el servidor no pueda recibir nuevas conexiones, pero no afectará la conexión establecida
2> Cerrar el socket conectado devuelto por accept hará que la conexión que representa se bloquee Cerrado, pero no afectará el monitoreo del servidor

3. Al trabajar como cliente

​Cerrar la conexión significa cerrar la conexión, no significa otra

1.5.2 Apretón de manos de tres vías
1.5.3 Ola cuatro veces
1.6 Servidor concurrente TCP

TCP no es originalmente un servidor concurrente y un servidor TCP solo puede comunicarse con un cliente a la vez.

Codigo original:

cliente.c

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

int main(int argc, char **argv){
    
    

	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		perror("fail to socket");
		exit(1);
	}
	
	//第二步:发送客户端连接请求
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(agrv[2]));
	
	if(connect(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		perror("fail to connect");
		exit(1);
	}
	
	//第三步:进行通信
	//发送数据
	char buf[128] = "";
	while(1){
    
    
		fgets(buf,128,stdin);
		buf[strlen(buf) - 1] = '\0';
		if(send(sockfd,buf,128,0) == -1){
    
    
			perror("fail to send");
			exit(1);
		}
		if(strncmp(buf,"quit",4) == 0){
    
    
			exit(0);
		}
		
		//接收数据
		char text[128] = "";
    	if(recv(sockfd,text,128,0) == -1){
    
    
    		perror("fail to recv");
    		exit(1);
    	}
    
  	  printf("from server: %s\n",text);
    }
   	//第四步:关闭套接字文件描述符
   	close(sockfd);
	
	return 0;
}

servidor.c

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

int main(int argc, char **argv)
{
    
    
	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		perror("fail to socket");
		exit(1);
	}
	
	//将套接字设置为允许重复使用本机地址或者设置为端口复用
	int on = 1;
	if(setsockopt(sockfd,SOL_SOCKET,SO)REUSEADDR,&on,sizeof(on) < 0){
    
    
		perror("fail to setsockopt");
		exit(1);
	}
	
	//第二步:将套接字与服务器网络信息结构体绑定
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		perror("fail to bind");
		exit(1);
	}
	
	//第三步:将套接字设置为被动监听状态
	if(listern(sockfd,10) == -1){
    
    
		perror("fail to listen");
		exit(1);
	}
	
	//第四步:阻塞等待客户端的连接请求
	int acceptfd;
	struct sockaddr_in clientaddr;
	if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen)) == -1){
    
    
		perror("fail to accept");
		exit(1);
	}
	//打印连接的客户端信息
	printf("ip:%s ,port:%d\n",inet_ntoa(clientaddr),ntohs(clientaddr.sin_port));
	
	//第五步:进行通信
	char buf[128] = "";
	ssize_t bytes;
	while(1){
    
    
		if(bytes = recv(acceptfd,buf,128,0) < 0){
    
    
			perror("fail to recv");
			exit(1);
		}
		else if(bytes == 0){
    
    
			printf("the clinet quited\n");
			exiy(1);
		}
		
		if(strncmp(buf,"quit",4) == 0){
    
    
			exit(0);
		}
	
		printf("from client: %s\n",buf);
	
		strcat(buf,"*_*");
		if(send(acceptfd,buf,128,0) == -1){
    
    
			perror("fail to send");
			exit(1);
		}
	}
	return 0;
}

La razón por la que TCP no puede lograr la concurrencia:
dado que el servidor TCP tiene dos funciones de bloqueo de lectura, aceptar y recibir, las dos funciones deben ejecutarse una después de la otra, por lo que la otra función no puede ejecutarse cuando una función se está ejecutando, por lo que hay no hay garantía de que el cliente se conectará al mismo tiempo mientras se comunica con otros clientes.

Cómo implementar un servidor TCP concurrente:
1. Use multiproceso para implementar un servidor TCP concurrente
2. Use multiproceso para implementar un servidor TCP concurrente

1.6.1 Multiproceso para lograr la concurrencia
int sockfd = socket();
bind()
listen()
while(1)
{
    
    
	accept()
	pid = fork();
	if(pid > 0)
	{
    
    
	
	}
	else if(pid == 0)
	{
    
    
		while(1)
		{
    
    
			recv()/send()
		}
	}
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

//使用多进程实现TCP并发服务器

#define N 128
#define ERR_LOG(errmsg)do{
      
      \										perror(errmsg);\
						exit(1);\
						}while(0)
						
void handler(int sig){
    
    
	wait(NULL);
}

int main(int argc, char **argv)
{
    
    
	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		ERR_LOG("fail to socket");
	}
	
	//将套接字设置为允许重复使用本机地址或者设置为端口复用
	int on = 1;
	if(setsockopt(sockfd,SOL_SOCKET,SO)REUSEADDR,&on,sizeof(on) < 0){
    
    
		ERR_LOG("fail to setsockopt");
	}
	
	//第二步:将套接字与服务器网络信息结构体绑定
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		ERR_LOG("fail to bind");
	}
	
	//第三步:将套接字设置为被动监听状态
	if(listern(sockfd,10) == -1){
    
    
		perror("fail to listen");
		exit(1);
	}
	
	//使用信号,异步的方式处理僵尸进程
	signal(SIGCHID,handler)
	
	while(1){
    
    
		//第四步:阻塞等待客户端的连接请求
		int acceptfd;
		struct sockaddr_in clientaddr;
		if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen)) == -1){
    
    
			perror("fail to accept");
			exit(1);
		}
		//打印连接的客户端信息
		printf("ip:%s ,port:%d\n",inet_ntoa(clientaddr),ntohs(clientaddr.sin_port));
	
		//第五步:使用fork函数创建子进程,父进程继续负责连接,子进程负责与客户端通信
		pid_t pid;
		if((pid = fork()) < 0){
    
    
			ERR_LOG("fail to fork");
		}
		else if(pid > 0){
    
    
			//父进程负责执行accept,所以if语句结束后继续在accept函数的位置阻塞
		}
		else{
    
    
			//子进程负责和指定的客户端通信
			char buf[128] = "";
			ssize_t bytes;
			while(1){
    
    
				if(bytes = recv(acceptfd,buf,128,0) < 0){
    
    
					perror("fail to recv");
					exit(1);
				}
				else if(bytes == 0){
    
    
					printf("the clinet quited\n");
					exit(1);
				}
		
				if(strncmp(buf,"quit",4) == 0){
    
    
					exit(0);
				}
	
				printf("from client: %s\n",buf);
	
				strcat(buf,"*_*");
				if(send(acceptfd,buf,128,0) == -1){
    
    
					perror("fail to send");
					exit(1);
				}
			}
		}
	}
	return 0;
}
1.6.2 Implementación de concurrencia multiproceso
void *thread_fun(void *arg){
    
    
	while(1){
    
    
		recv()/send()
	}
}

sockfd = socket()
bind()
listen()
while(1)
{
    
    
	accept()
	//只要有客户端连接上,则创建一个子线程与之通信
	pthread_create(&thread,NULL,thread_fun,...)
	pthread_detach();	
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

//使用多进程实现TCP并发服务器

#define N 128
#define ERR_LOG(errmsg)do{
      
      \										perror(errmsg);\
						exit(1);\
						}while(0)
						
typedef struct{
    
    
	struct sockaddr_in addr;
	int acceptfd;
}MSG;

void *pthread_fun(void *arg){
    
    
	char buf[N] = "";
	ssize_t bytes;
	MSG msg = *(MSG *)arg;
	while(1){
    
    
		if(bytes = recv(msg.acceptfd,buf,128,0) < 0){
    
    
			perror("fail to recv");
			exit(1);
		}
		else if(bytes == 0){
    
    
			printf("the clinet quited\n");
			exit(1);
		}
		
		if(strncmp(buf,"quit",4) == 0){
    
    
			printf("The client quited\n");
			pthread_exit(NULL);
		}
		printf("[%s - %d]: %s\n",inet_ntoa(msg.addr.sin_addr),ntohs(msg.addr.sin_port),buf);
		strcat(buf,"*_*");
		if(send(msg.acceptfd,buf,128,0) == -1){
    
    
			perror("fail to send");
			exit(1);
		}
	}
}

int main(int argc, char **argv)
{
    
    
	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		ERR_LOG("fail to socket");
	}
	
	//将套接字设置为允许重复使用本机地址或者设置为端口复用
	int on = 1;
	if(setsockopt(sockfd,SOL_SOCKET,SO)REUSEADDR,&on,sizeof(on) < 0){
    
    
		ERR_LOG("fail to setsockopt");
	}
	
	//第二步:将套接字与服务器网络信息结构体绑定
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		ERR_LOG("fail to bind");
	}
	
	//第三步:将套接字设置为被动监听状态
	if(listern(sockfd,10) == -1){
    
    
		perror("fail to listen");
		exit(1);
	}
	
	//使用信号,异步的方式处理僵尸进程
	signal(SIGCHID,handler)
	
	while(1){
    
    
		//第四步:阻塞等待客户端的连接请求
		int acceptfd;
		struct sockaddr_in clientaddr;
		if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen)) == -1){
    
    
			perror("fail to accept");
			exit(1);
		}
		//打印连接的客户端信息
		printf("ip:%s ,port:%d\n",inet_ntoa(clientaddr),ntohs(clientaddr.sin_port));
	
		//创建子线程与客户端进行通信
		MSG msg;
		msg.addr = clientaddr;
		msg.acceptfd = acceptfd;
		pthread_t thread;
		if(pthread_create(&thread,NULL,pthread_fun,&msg) != 0){
    
    
			ERR_LOG("fail to pthread_create");
		}
		pthread_detach(thread);	
		}
	}
	return 0;
}
1.7 Introducción al servidor web
1.7.1 Introducción al servidor web

El servidor web también se denomina servidor WWW, servidor de sitio web, etc.

特点:
使用HTTP协议与客户机浏览器进行信息交流
不仅能存储信息,还能在用户通过web浏览器提供的信息的基础上运行脚本和程序
该服务器可安装在UNIX、Linux或Windows等操作系统上
著名的服务器有Apache、Tomcat、IIS等

1.7.2 HTTP协议

Webserver—HTTP协议(超文本协议)

概念:一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议

特点:
1.支持C/S架构
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径,常用方法:GET、POST
3.无连接:限制每次连接只处理一个请求

1.7.3 Webserver通信过程
1.7.4 Web编程开发

网页浏览(使用过GET方式)

客户端浏览请求

Web服务器的ip地址是192.168.3.103,端口号是9999,要访问的网页时about.html

服务器收到的数据:

GET/index.html HTTP/1.1
Accept:image/gif.image/jpeg,*/*
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
Accept-Encoding:gzip,deflate

服务器应答的格式:

服务器接收到浏览器发送的数据之后,需要判断GET/后面跟的网页是否存在,如果存在则请求成功,发送指定的指令,并发送文件内容给浏览器,如果不存在,则发送请求失败的指令

请求成功

"HTTP/1.1 200 OK\r\n"				\
"Content-Type: text/html\r\n"	\
"\r\n";

请求失败

"HTTP/1.1 404 Not Found\r\n"		\
"Content-Type: text/html\r\n"		\
"\r\n"							  \
"<HTML><BODY>File not found</BODY></HTML>"

案例:

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

//使用多进程实现TCP并发服务器

#define N 128
#define ERR_LOG(errmsg)do{
      
      \			perror(errmsg);\
						exit(1);\
						}while(0)

void *pthread_fun(void *arg){
    
    
	int acceptfd = *(int *)arg;
	char buf[N] = "";
	char head[] = "HTTP/1.1 200 OK\r\n"				\
				"Content-Type: text/html\r\n"		\
				"\r\n";
	char err[] = "HTTP/1.1 404 Not Found\r\n"		\
				"Content-Type: text/html\r\n"	\
				"\r\n"						\
				"<HTML><BODY>File not found</BODY></HTML>";
	//接收浏览器通过http协议发送的数据包
	if(recv(acceptfd,buf,N,0) < 0){
    
    
		ERR_LOG("fail to recv");
	}
	
	printf("******************\n\n");
	printf("%s\n",buf);
	
	//通过获取的数据包中得到浏览器要访问的网络文件名
	// GET /about.html http/1.1
	char filename[128] = "";
	sscanf(buf,"GET /%s",filename);   //sscanf函数遇空格结束,所以直接可以获取文件名
	if(strncmp(filename,"HTTP/1.1",strlen("http/1.1")) == 0){
    
    
		strcpy(filename,"about.html");
	}
	printf("filename = %s\n",filename);
	
	char path[128] = "./sqlite/";
	strcat(path,filename);
	
	//通过解析出来的网页文件名,查找本地中有没有这个文件
	int fd;
	if((fd = open(path,O_RDONLY)) < 0){
    
    
		//如果文件不存在,则发送不存在对应的指令
		if(errno == ENOENT)
		{
    
    
			if(send(acceptfd,err,strlen(err),0) < 0){
    
    
				ERR_LOG("fail to send");
			}
			close(acceptfd);
			pthread_exit(NULL);
		}
		else{
    
    
			ERR_LOG("fail to open");
		}
	}
	
	//如果文件存在,先发送指令告知浏览器
	if(send(acceptfd,head,strlen(head),0) < 0){
    
    
		ERR_LOG("fail to send");
	}
	//读取网页文件中的内容并发送给浏览器
	ssize_t bytes;
	char text[1024] = "";
	while((bytes = read(fd,text,1024)) > 0){
    
    
		if(send(acceptfd,text,bytes,0) < 0){
    
    
			ERR_LOG("fail to send");
		}
	}
	pthread_exit(NULL);
}

int main(int argc, char **argv)
{
    
    
	if(argc < 3){
    
    
		fprintf(stderr,"UseageL %s [ip] [port]\n",argv[0]);
	}
	//第一步:通过socket函数创建一个TCP套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
    
    
		ERR_LOG("fail to socket");
	}
	
	//将套接字设置为允许重复使用本机地址或者设置为端口复用
	int on = 1;
	if(setsockopt(sockfd,SOL_SOCKET,SO)REUSEADDR,&on,sizeof(on) < 0){
    
    
		ERR_LOG("fail to setsockopt");
	}
	
	//第二步:将套接字与服务器网络信息结构体绑定
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen) == -1)
	{
    
    
		ERR_LOG("fail to bind");
	}
	
	//第三步:将套接字设置为被动监听状态
	if(listern(sockfd,10) == -1){
    
    
		perror("fail to listen");
		exit(1);
	}
	
	//使用信号,异步的方式处理僵尸进程
	signal(SIGCHID,handler)
	
	while(1){
    
    
		//第四步:阻塞等待客户端的连接请求
		int acceptfd;
		struct sockaddr_in clientaddr;
		if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen)) == -1){
    
    
			perror("fail to accept");
			exit(1);
		}
		//打印连接的客户端信息
		printf("ip:%s ,port:%d\n",inet_ntoa(clientaddr),ntohs(clientaddr.sin_port));
	
		//创建子线程与客户端进行通信
		MSG msg;
		msg.addr = clientaddr;
		msg.acceptfd = acceptfd;
		pthread_t thread;
		if(pthread_create(&thread,NULL,pthread_fun,&msg) != 0){
    
    
			ERR_LOG("fail to pthread_create");
		}
		pthread_detach(thread);	
		}
	}
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/AAAA202012/article/details/127391381
Recomendado
Clasificación