Linux网络编程笔记(三)TCP编程模型

文章目录

  • 参考
  • 1 TCP客户端服务器编程模型
    • 1.1 客户端调用序列
    • 1.2 服务器端调用序列
  • 2 套接字与地址绑定
    • 2.1 绑定地址
    • 2.2 特殊bind地址
    • 2.3 查找绑定到套接字的地址
    • 2.4 获取对方地址
  • 3 建立连接
    • 3.1 服务器端
    • 3.2 客户端
  • 4 示例
    • 4.1 服务器端编程
    • 4.2 客户端编程
    • 4.3 运行结果

参考

本文所有知识均参考网课:
https://study.163.com/course/courseLearn.htm?courseId=1002913013&share=1&shareId=1145943119#/learn/video?lessonId=1003300265&courseId=1002913013

1 TCP客户端服务器编程模型

Linux网络编程笔记(三)TCP编程模型_第1张图片
如上图所示,

1.1 客户端调用序列

  • 调用socket函数创建套接字
  • 调用connect连接服务器端
  • 调用I/O函数(read/write)与服务器端通讯
  • 调用close关闭套接字

1.2 服务器端调用序列

  • 调用socket函数创建套接字
  • 调用bind绑定本地地址和端口
  • 调用listen启动监听
  • 调用accept从已连接列队中提取客户连接
  • 调用I/O函数(read/write)与客户端通讯
  • 调用close关闭套接字

2 套接字与地址绑定

2.1 绑定地址

#include
/*
参数:
	sockfd:套接字描述符
	addr:通用地址结构指针
	len:addr指向地址的长度
返回:成功返回0,出错返回-1
*/
int bind(int sockfd,const struct sockaddr *addr,socklen_t len);
	//返回:成功返回0,出错返回-1

2.2 特殊bind地址

一台主机可以有多个网络接口和多个IP地址,如果我们只关心某个地址的连接请求,我们可以指定一个具体的本地IP地址,如果要响应所有接口上的连接请求就要使用一个特殊的地址:INADDR_ANY。

#define INADDR_ANY (uint32_t)0x00000000
//监听所有服务器上ip得到的连接请求
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_addr.s_addr = INADDR_ANY;

2.3 查找绑定到套接字的地址

#include
/*
参数:
	sockfd:套接字描述符
	addr:通用地址结构指针
	alenp:addr指向地址的长度
返回:成功返回0,出错返回-1
*/
int getsockname(int sockfd,struct sockaddr *restrict addr,socklen_t *restrict alenp);

2.4 获取对方地址

#include
/*
参数:
	sockfd:套接字描述符
	addr:通用地址结构指针
	alenp:addr指向地址的长度
返回:成功返回0,出错返回-1
*/
int getpeername(int sockfd,struct sockaddr *restrict addr,socklen_t *restrict alenp);

3 建立连接

3.1 服务器端

#include
/*
参数:
	sockfd:套接字描述符
	backlog:指定进行客户端连接派对的队列长度
	addr:通用地址结构指针
	len:addr指向地址的长度
返回:成功返回0,出错返回-1
*/
int listen(int sockfd,int backlog); //调用listen函数启动监听(指定port监听)
									//通知系统去接受来自客户端的连接请求,
									//同时将接收到的客户端连接请求放置到对应的队列中

//调用accept函数从队列中获得一个客户端的请求连接,并返回新的socket描述符,若没有客户端连接,调用此函数会阻塞,直到获得一个客户端的连接					
int accept(int sockfd,struct sockaddr *restrict addr,socklen_t *restrict len);

3.2 客户端

#include
/*
参数:
	sockfd:套接字描述符
	addr:通用地址结构指针
	len:addr指向地址的长度
返回:成功返回0,出错返回-1
*/
int connect(int sockfd,const struct sockaddr *addr,socklen_t len);

4 示例

4.1 服务器端编程

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int sockfd;

void sig_handler(int signo){
	if(signo==SIGINT){
		printf("server close\n");
		close(sockfd);
		exit(1);
	}
}

/*输出连接上来的客户端相关信息*/
void out_addr(struct sockaddr_in *clientaddr){
	//将端口从网络字节序转换成主机字节序
	int port = ntohs(clientaddr->sin_port);
	char ip[16];
	memset(ip,0,sizeof(ip));
	//将ip地址从网络字节序转换成点分十进制
	inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));
	printf("client:%s(%d) connected\n",ip,port);
}

void do_service(int fd){
	//获得系统时间
	long t = time(0); 
	char *s = ctime(&t); 
	size_t size = strlen(s) * sizeof(char);
	//将服务器端获得的系统时间写回到客户端
	if(write(fd,s,size) != size){
		perror("write error");
	}
}
int main(int argc,char* argv[])
{
	if(argc<2){
		printf("usage:%s #port\n",argv[0]);
		exit(1);
	}
	if(signal(SIGINT,sig_handler)==SIG_ERR){
		perror("signal sigint error");
		exit(1);
	}

	/*步骤1:创建socket*/
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0){
		perror("socket error");
		exit(1);	
	}

	/*步骤2:调用bind函数将socket和地址(包括ip、port)进行绑定
	 * */
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	//往地址中填入port、internet地址族类型、ip
	serveraddr.sin_family = AF_INET; //IPV$
	serveraddr.sin_port = htons(atoi(argv[1])); //端口需要网络字节序
	serveraddr.sin_addr.s_addr = INADDR_ANY;
	if(bind(sockfd,(struct sockaddr*) &serveraddr,sizeof(serveraddr))<0){
		perror("bind error");
		exit(1);
	}

	/*步骤3:调用listen函数启动监听(指定port监听)*/
	if(listen(sockfd,10)<0){
	 	perror("listen error");
		exit(1);
	}

	/*步骤4:调用accept函数从队列中获得一个客户端的请求连接*/
	struct sockaddr_in clientaddr;
	socklen_t clientaddr_len = sizeof(clientaddr);
	while(1){
		int fd = accept(sockfd,(struct sockaddr*)&clientaddr,
				&clientaddr_len);
		if(fd<0){
			perror("accept error");
			continue;
		}

		/*步骤5:调用IO函数(read/write)和连接的客户端进行双向通信*/
       		out_addr(&clientaddr);
       		do_service(fd);

       		/*步骤6:关闭socket*/
       		close(fd);

	}


	return 0;
}

4.2 客户端编程

#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(int argc,char *argv[]){
	if(argc<3){
		printf("usage: %s ip port\n",argv[0]);
		exit(1);
	}

	/*步骤1:创建socket*/
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0){
		perror("socket error");
		exit(1);
	}
	
	//往serveraddr中填入地址族类型、port和ip
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(atoi(argv[2]));
	//将ip地址转换成网络字节序后填入sereraddr中
	inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);

	/*步骤2:客户端调用connect函数连接到服务器端*/
	if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
		perror("connect error");
		exit(1);
	}

	/*步骤3:调用IO函数(read/write)和服务器端进行双向通信*/
	char buffer[1024];
	memset(buffer,0,sizeof(buffer));
	size_t size;
	if((size = read(sockfd,buffer,sizeof(buffer))) < 0){
		perror("read error");
	}
	if(write(STDOUT_FILENO,buffer,size) != size){
		perror("write error");
	}


	/*步骤4:关闭socket*/
	close(sockfd);

	return 0;

}

4.3 运行结果

以下都在本机测试:
1 启动服务器端监听:
在这里插入图片描述
2 启动客户端连接,同时服务器端接收到客户端的连接:
在这里插入图片描述在这里插入图片描述

你可能感兴趣的:(linux网络编程)