我是靠谱客的博主 外向煎饼,最近开发中收集的这篇文章主要介绍TCP编程详解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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] = '';
	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: %sn",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:%dn",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: %sn",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] = '';
		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: %sn",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:%dn",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 quitedn");
			exiy(1);
		}
		
		if(strncmp(buf,"quit",4) == 0){
			exit(0);
		}
	
		printf("from client: %sn",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:%dn",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 quitedn");
					exit(1);
				}
		
				if(strncmp(buf,"quit",4) == 0){
					exit(0);
				}
	
				printf("from client: %sn",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 quitedn");
			exit(1);
		}
		
		if(strncmp(buf,"quit",4) == 0){
			printf("The client quitedn");
			pthread_exit(NULL);
		}
		printf("[%s - %d]: %sn",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:%dn",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 OKrn"				
"Content-Type: text/htmlrn"	
"rn";

请求失败

"HTTP/1.1 404 Not Foundrn"		
"Content-Type: text/htmlrn"		
"rn"							  
"<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 OKrn"				
				"Content-Type: text/htmlrn"		
				"rn";
	char err[] = "HTTP/1.1 404 Not Foundrn"		
				"Content-Type: text/htmlrn"	
				"rn"						
				"<HTML><BODY>File not found</BODY></HTML>";
	//接收浏览器通过http协议发送的数据包
	if(recv(acceptfd,buf,N,0) < 0){
		ERR_LOG("fail to recv");
	}
	
	printf("******************nn");
	printf("%sn",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 = %sn",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:%dn",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;
}

最后

以上就是外向煎饼为你收集整理的TCP编程详解的全部内容,希望文章能够帮你解决TCP编程详解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部