Detailed explanation of TCP programming

1.1 TCP introduction, programming process

TCP review:
​ 1. Connection-oriented streaming protocol, reliable, error retransmission, and a corresponding confirmation must be given every time a data is received;​ 2. A connection needs to be
established before communication;
​ 3. The server is a passive connection, The client is an active link

The difference between TCP and UDP
:

TCP C/S Architecture

TCP programming process

Server:
​ Create a socket socket()
​ Bind the socket to the server network information structure bind()
​ Set the socket to the listening state listen()
​ Block and wait for the client's connection request accept() (blocking function)
to communicate recv()/send()
to close the socket close()

Client:
Create socket socket()
Send client connection request connect()
Communicate send()/recv()
Close socket close()

1.2 TCP programming—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

case:

#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 connect、send、recv
1.3.1 connect function
#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

Note:
1. Connect will not generate a new socket after the connection is established
2. TCP data can only be transmitted after the connection is successful
3. Header file: #include<sys/socket.h>

1.3.2 send function
#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

Note:
You cannot send 0-length data packets with the TCP protocol

1.3.3 recv function
#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 Client code

Use the network debugging assistant under windows as the server

Client's code:

#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 TCP server—bind, listen, accept
1.4.1 Conditions required as a TCP server

1. Have a known address;
2. Let the operating system know that it is a server, not a client;
3. Wait for the connection to come

For the connection-oriented TCP protocol, the establishment of a connection really means the beginning of data communication.

1.4.2 bind function
#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 listen function
#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);
功能:
	将套接字由主动修改为被动
	使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接
参数:
	sockfd:	socket监听套接字
	backlog:连接队列的长度
返回值:
	成功:返回0
	失败:其他
1.4.4 accept function
#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 TCP server example
#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 TCP programming—close, three-way handshake, four-way wave
1.5.1 close closes the socket

1. Use the close function to close the socket (closing a connected socket will cause the other end to receive a 0-length data packet)

2. When working as a server
1> Closing the listening socket will cause the server to be unable to receive new connections, but will not affect the established connection
2> Closing the connected socket returned by accept will cause the connection it represents to be blocked Closed, but will not affect the server's monitoring

3. When working as a client

​Closing the connection means closing the connection, it does not mean other

1.5.2 Three-way handshake
1.5.3 Wave four times
1.6 TCP concurrent server

TCP is not originally a concurrent server, and a TCP server can only communicate with one client at a time.

Original code:

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

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

The reason why TCP cannot achieve concurrency:
Since the TCP server has two read blocking functions, accept and recv, the two functions need to be run one after the other, so the other function cannot be executed when one function is running, so there is no guarantee that the client will be connected at the same time. while communicating with other clients.

How to implement a TCP concurrent server:
1. Use multi-process to implement a TCP concurrent server
2. Use multi-thread to implement a TCP concurrent server

1.6.1 Multi-process to achieve concurrency
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 Multithreaded Concurrency Implementation
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 Web server introduction
1.7.1 Introduction to web server

Web server is also called WWW server, website server, 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;
}

Guess you like

Origin blog.csdn.net/AAAA202012/article/details/127391381
Recommended