TCP编程详解

16 篇文章 3 订阅
订阅专栏
1.1 TCP介绍、编程流程

TCP回顾:
​ 1.面向连接的流式协议,可靠、出错重传、且每收到一个数据都要给出相应的确认;
​ 2.通信之前需要建立链接;
​ 3.服务器是被动链接,客户端是主动链接

TCP与UDP的差异:

TCP C/S架构

TCP编程流程

服务器:
​ 创建套接字socket()
​ 将套接字与服务器网络信息结构体绑定bind()
​ 将套接字设置为监听状态listen()
​ 阻塞等待客户端的连接请求accept()(阻塞函数)
​ 进行通信recv()/send()
​ 关闭套接字close()

客户端:
​ 创建套接字socket()
​ 发送客户端连接请求connect()
​ 进行通信send()/recv()
​ 关闭套接字close()

1.2 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

案例:

#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函数
#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

注意:
​ 1.connect建立连接之后不会产生新的套接字
​ 2.连接成功后才可以开始传输TCP数据
​ 3.头文件:#include<sys/socket.h>

1.3.2 send函数
#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

注意:
​ 不能用TCP协议发送0长度的数据包

1.3.3 recv函数
#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 客户端代码

使用windows下的网络调试助手作为服务器

客户端的代码:

#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服务器—bind、listen、accept
1.4.1 作为TCP服务器需要具备的条件

1.具备一个可以确知的地址;
2.让操作系统知道是一个服务器,而不是客户端;
3.等待连接的到来

对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始。

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

int listen(int sockfd,int backlog);
功能:
	将套接字由主动修改为被动
	使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接
参数:
	sockfd:	socket监听套接字
	backlog:连接队列的长度
返回值:
	成功:返回0
	失败:其他
1.4.4 accept函数
#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服务器例子
#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编程—close、三次握手、四次挥手
1.5.1 close关闭套接字

1.使用close函数即可关闭套接字(关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包)

2.做服务器时
​ 1>关闭监听套接字将导致服务器无法接收到新的连接,但不会影响已经建立的连接
​ 2>关闭accept返回的已连接套接字将导致他所代表的连接被关闭,但不会影响服务器的监听

3.做客户端时

关闭连接就是关闭连接,不意味着其他

1.5.2 三次握手
1.5.3 四次挥手
1.6 TCP并发服务器

TCP原本不是并发服务器,TCP服务器同一时间只能与一个客户端通信。

原始代码:

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

TCP不能实现并发的原因:
由于TCP服务器端有两个读阻塞函数,accept和recv,两个函数需要先后运行,所以导致运行一个函数的时候另一个函数无法执行,所以无法保证一边连接客户端,一边与其他客户端通信。

如何实现TCP并发服务器:
1.使用多进程实现TCP并发服务器
2.使用多线程实现TCP并发服务器

1.6.1 多进程实现并发
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 多线程并发实现
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服务器介绍
1.7.1 web服务器简介

Web服务器又称WWW服务器、网站服务器等

特点:
使用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;
}
详解python tcp编程
09-16
Python TCP编程网络通信的基础,它涉及到传输控制协议TCP,这是互联网协议栈中用于可靠数据传输的重要部分。TCP是一种面向连接的协议,提供了全双工通信,确保数据的顺序传输和错误校验,保证了数据的完整性。 在...
TCP协议详解
07-21
TCP协议详解,连接的3次握手、断开的4次握手、如何保证可靠传输(ack的应答)。
TCP网络编程
weixin_47783699的博客
11-20 654
TCP网络编程,实现客户端 与 网络助手 相互通信
网络编程套接字(3): 简单的TCP网络程序
Ryujianli的博客
08-28 550
网络编程套接字(3): 简单的TCP网络程序
2024年TCP协议漫谈,深入浅出实战网络编程开发,带给你不一样的认知,菜鸟网络java面试题
最新发布
2401_84023413的博客
05-06 848
afka的生产者9、选项(Option):长度不定,但长度必须以是32bits的整数倍。常见的选项包括MSS、SACK、Timestamp等等。关于TCP的状态机理解我们从几张经典的示意图开始TCP三次握手、四次挥手时序图。
简单TCP通信网络编程
qq_62821433的博客
12-29 1114
简单TCP通信的案例,通过流套接字实现。建立连接的过程包括三次握手,即客户端发送SYN请求,服务器响应SYN-ACK,客户端再次确认ACK。断开连接的过程包括四次挥手,即客户端发送FIN请求,服务器响应ACK,服务器发送FIN请求,客户端响应ACK。
网络编程:(C语言)搭建TCP服务器
SYF_wdnmd的博客
05-17 620
适用场景:对干传输质量比较高,以及传输大量数据的通信。在需要可靠通信的数据传输场景例如:账户充值,账户登录。i.为了提高效率,TCP会将多个足够小,且发送间隔短的数据包,粘成一个包发送,该现象称之为粘包现象。i.TCP会给每个数据包都编上一个编号,这个编号称之为序列号。b.数据无误,数据无丢失,数据无失序,数据无重复到达的通信。a.TCP是提供面向连接的,稳定的可靠的数据传输协议。TCP特点:---- 稳定,是一种传输层协议。d.数据的收发是不同步的。c.传输效率低,耗费资源多。
Windows下TCP编程——服务器客户端代码以及爬虫示例
AAAA202012的博客
10-20 1983
为什么Ieee浏览器能显示网站的照片等数据呢,是因为浏览器(客户端)给这个网站(服务器)发了一个请求,请求他的数据给我,这个网站服务器就将它的数据就发到我的这个Ieee上来,当然也能发到我写的程序。个人电脑上打了一个网址(www.xxxxxx),IE会生成一段码流(http协议的格式),服务器将请求的结果(网页源代码)返回给IE,IE拿了源代码之后,根据其中的链接,再去各服务器获取图片内容。而我们的爬虫就是模拟IEEE生成一段码流,发送给服务器我们的请求,服务器将请求的结果返回。
【C语言实现windows环境下Socket编程TCP/IP协议】
ccccls的博客
12-06 1670
在Windows环境下,C语言利用Socket实现TCP/IP通信
Python基础教程之tcp socket编程详解及简单实例
09-21
主要介绍了Python基础教程之tcp socket编程详解及简单实例的相关资料,需要的朋友可以参考下
Linux下tcp通信程序
12-08
Linux下tcp通信程序,含客户端和服务器以及Makefile,多线程-可同时收发操作。 不足:退出机制不太完整。
网络编程基础一:TCP/IP 协议详解
Mr_lisj的博客
03-29 2813
前言 我们知道两个进程如果需要进行通讯最基本的一个前提【能够唯一的标示一个进程】,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。能够唯一标示网络中的进...
基本TCP套接字常用函数及参数详情
Primeprime的专栏
12-19 1049
1.socket()函数 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); domain: 协议域 type: 套接字类型 protocol: 某协议类型常值 domain参数的常用值 Na...
Tcp/Ip协议
qq_58720080的博客
07-22 643
TCP/Ip网络通信
TCP服务器代码讲解
weixin_46098612的博客
02-16 1233
本人手撕一段,很多地方没有判断清楚,不利于调试 /* ************************************************************************ * Filename: 01tcp_server.c * Description: * Version: 1.0 * Created: 2019骞?2鏈?2鏃?10鏃?2鍒?7绉? * Revision: none *
TCP编程流程
anton_99的博客
06-09 2139
TCP是传输控制协议的简称,是TCP/IP体系中的面向连接的传输层协议,在网络中提供全双工的、可靠的服务。在上一章节中可知,TCP通信是通过套接字通信机制实现的,具体为流式套接字,用来实现一个面向连接,可靠的数据传输服务。目前较为流行的网络编程模型是客户端、服务器的通信模式。服务器和客户端使用TCP通信的流程如下图8.1所示。 图8.1 使用TCP时套接字编程流程 socket编程的基本函数有s...
Socket编程(C语言实现)——TCP协议(网络间通信AF_INET)的流式(SOCK_STREAM)+报式(SOCK_DGRAM)传输【多线程+循环监听】
热门推荐
杰儿__er 的博客
07-29 7万+
Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求。如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服务器进程B1同时为客户进程A1、A2和B2提供服务。 Socket概述 ① 所谓Socket通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过“套接字”...
TCP通信】原理详解编程实现(一)
qq_43145072的博客
04-16 1万+
TCP简介 TCP把连接作为最基本的抽象单元,每条TCP连接有两个端点,TCP连接的端点即套接字。 套接字socket = (IP地址+端口号) TCP连接={socket1,socket2}={(IP1:port1),(IP2,port2)} TCP提供全双工通信。 TCP报文 首部中的重要概念 序号:Seq序号,占32位。用于说明当前数据第一个字节在所有数据(整个文件)中的位置 确认号:...
ocket connection to 'ws://localhost:2222/websocket' failed: main @ index.js:6
05-23
这个错误信息提示是无法建立 WebSocket 连接到本地主机的 2222 端口。可能是以下原因导致的: 1. 目标主机或端口不正确。 2. 网络连接存在问题。 3. WebSocket 服务器没有正确启动或正在使用其他端口。 你可以先检查一下目标主机和端口是否正确,并确保网络连接正常。如果问题仍然存在,可以检查一下 WebSocket 服务器的配置和日志,以查看是否有其他问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 前方交会与后方交会 22170
  • Linux系统基础知识 21970
  • 相对定向与绝对定向 20603
  • Qt类总结(一)QTextEdit 15411
  • 3D基础概念 13611

分类专栏

  • 实践小项目 付费
  • 机器 视觉 1篇
  • 小知识 6篇
  • 摄影测量 5篇
  • 编程 9篇
  • 数据结构 2篇
  • 计算机网络编程 16篇
  • CUDA 39篇
  • C++ 7篇
  • WIN32 2篇
  • qt 25篇
  • MFC 1篇
  • linux 1篇
  • 三维重建代码学习 8篇
  • 文献阅读 2篇
  • 三维重建 22篇

最新评论

  • Qt实战(四)——图片浏览器

    wkeor: 大佬求源码,1925055140@qq.com

  • 摄影测量——内定向

    m0_74774279: 这篇调用的函数可以分享一下吗?

  • 核线影像生成(摄影测量)以及极线校正(计算机视觉)

    m0_74774279: 这个调用函数您找到了嘛

  • Qt实战(四)——图片浏览器

    2401_84239574: 大佬,大佬,求源码,十分感谢!!!!2483873985@qq.com

  • 相对定向与绝对定向

    Chloe: 大佬 补代码 大佬是不是忘记补代码了 大佬佬佬佬佬

大家在看

  • 深入探索Python中的回文检测:算法与实现 353
  • 贪吃蛇游戏代码(C语言项目) 1122

最新文章

  • Qt里的信号与槽原理
  • QJsonObject 类
  • QT里的网络通信简介
2023年12篇
2022年60篇
2021年65篇

目录

目录

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳SEO优化公司同乐seo网站推广宝安网站推广工具南澳SEO按天扣费大芬英文网站建设盐田关键词排名包年推广龙华优秀网站设计松岗SEO按天收费宝安网站设计惠州企业网站设计南山网站优化西乡优化龙华seo网站推广坪地企业网站设计横岗网站排名优化布吉百搜标王丹竹头百姓网标王推广福田网站优化软件吉祥网站开发南山关键词按天扣费同乐网站定制平湖网站建设观澜百度网站优化排名松岗网站制作设计罗湖阿里店铺托管大浪营销型网站建设深圳网络推广民治SEO按天扣费荷坳企业网站设计大浪网络营销南联网站seo优化歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化