欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

网络编程2——CS模型的TCP通信流程总结 及 server、client的实现

程序员文章站 2022-06-14 10:27:14
...

一、socket模型创建流程图

网络编程2——CS模型的TCP通信流程总结 及 server、client的实现

二、server的实现

1,报错文件先写好,基本头文件写好,C的、网络的
2,创建socket,定义文件描述符lfd,记得要返回值检查
3,创建bind函数,其中第二个参数是服务器端地址结构,要定义及初始化并在传参时要强制转换
4,创建listen函数,传入文件描述符和允许同时接入的个数
5,创建accept函数,第二个参数是传入的已连接的客户端地址结构,因此也要定义一个客户端地址结构(不用初始化,因为是传进来的),传入时同样要强制转换,man查看手册的时候会发现accept函数的第三个参数是一个socklen_t类型的客户地址结构长度,因此也要定义一个clit_addr_len,accept函数返回值也记得检查
6,accept成功之后就开始等待连接,连接成功就可以从缓冲区中读取read待处理字符串,进行操作并写回write缓冲区
7,重要的最后一步,别忘记close文件描述符

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<ctype.h>
#define SERV_PORT 9527//定义一个端口号

void sys_err(const char * str)
{
	perror(str);
	exit(1);
}

int main(int argc, char *argv[])
{
	int lfd = 0;
	int cfd = 0;//accept返回的用于创建连接的socket
	int ret;//read会返回读到的实际字节数
	char buf[BUFSIZ];//read、write需要缓冲区,从读写到缓冲区的,一般用一个系统自带宏定义缓冲区大小,4096
	
	struct sockaddr_in serv_addr;//定义服务器端的地址结构
	struct sockaddr_in clit_addr;//定义客户端的地址结构,在accept的时候要做参数(其实可以和上一行一起写,但是第一次写还是清楚点吧
	socklen_t clit_addr_len;//定义客户端传入传出参数,accept的第三个参数,socklen_t 这是个类型,man accept中的参数类型不要弄错
	//初始化地址结构
	serv_addr.sin_family = AF_INET;//跟socket的第一个参数一样,表示IPv4还是6
	serv_addr.sin_port =htons(SERV_PORT)//不能直接放宏,要讲本地字节序转换成网络字节序 
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	lfd = socket(AF_INET, SOCK_STREAM,0);
	if(lfd == -1)//函数返回值一定要检查
	{
		sys_err("socket error");
	}
	//创建成功就得到一个文件描述符,接下来就是bind,给socket绑定地址结构
	//bing(文件描述符,地质结构的指针,地址结构长度)
	bind(lfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr));//注意第二个参数 地址结构的强转
	//bind之后listen建立最大同时通信数,再accept阻塞客户端建立连接
	listen(lfd, 128);
	clit_addr_len = sizeof(clit_addr);
	cfd = accept(lfd,(struct sockaddr*)(&clit_addr),&clit_addr_len)//注意第三个参数是传入传出参数,因此首先要有个传入值,accept会返回一个新的文件描述符
	if(cfd ==-1)
	{
		sys_err("accept error");
	}
	//创建成功,服务器就开始从客户端读内容(此处为小写字母)
	while(1){
		ret = read(cfd, buf,sizeof(buf));//读到buf中,ret就是实际读到的字节数
		write(STDOUT_FILED, buf, ret);//写到屏幕上,不然看不见
		for(int i = 0; i<ret; ++i)
		{
			buf[i] = toupper(buf[i]);//注意用到一个新函数就要去看看该函数的头文件ctype.h
			write(cfd, buf, ret);
		}//这就转换完毕,再给写回到buf中
	}
	close(lfd);
	close(cfd);
	
}

写个makefile调试一下,没错就可以启动服务器./server,启动了服务器就开始阻塞等待连接,此时我们还没有写客户端,可以先用nc命令试一下 nc = net connect
nc IP地址 端口号(此处是9527)
光标闪烁即开始通信,就可以输入一串小写字母看是否会返回大写字符串
网络编程2——CS模型的TCP通信流程总结 及 server、client的实现

三、获取客户端地址结构

客户端跟服务器成功建立连接可以得到客户端建立连接的IP地址和端口号,因为accept函数的第二个参数就是客户端的地址结构,因此我们可以得到客户端信息:用IP地址转换函数得到IP地址,通过转换字节序函数得到端口号(ntohs)√
网络编程2——CS模型的TCP通信流程总结 及 server、client的实现网络字节序和本地字节序的转换,点分十进制是本地字节序
inet_ntop():输入是地址结构得到ip地址,网络转换成本地
inet_pton():输入是IP地址得到网络字节序形式,本地转换成网络

四、client的实现

1,建立客户端socket,一个cfd接收,并检查返回值
2,客户端不用bind,直接就可以connect,man手册查看connect参数列表
3,connect函数的第二个参数时服务器地址结构,因此要定义(已知的)服务端地址结构,
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);//服务器端口
inet_pton(AF_INET,"127.0.0.1",&ser_addr.sin_addr.s_addr);//用IP地址转换函数得到网络字节序的服务器ip地址

#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define SERV_PORT 9527//服务器端口号

void sys_err(const char* str)
{
	perror(str);
	exit(1);
}
int main()
{
	int cfd;
	int ret;//connect要有一个接收值
	int counter = 10;
	char buf[BUFSIZ];//read的时候是从缓冲区中读的,默认BUFSIZ大小
	struct sockaddr_in serv_addr;//服务器地址结构
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET,"127.0.0.1",&ser_addr.sin_addr.s_addr);
	
	cfd = socket(AF_INET, SOCK_STREAM,0);//
	if(cfd ==-1)
		sys_err("socket error");
	ret = connect(cfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));//注意参数类型,是否需要强制转换。成功返回0
	if(ret != 0 )
		sys_err("connect err");

//连接成功,就可以往socket写数据
	while(--counter)
	{
		write(cfd, "hello",5);
		//服务器会有返回值
		ret = read(cfd, buf,sizeof(buf));//读到buf中,ret是读到的字节数
		write(STDOUT_FILED,buf,ret);
	}
	close(cfd);
	return 0;
	
}

放到同一个目录下,起服务器,起客户端即可
网络编程2——CS模型的TCP通信流程总结 及 server、client的实现

五、TCP通信流程(假设是实现大小写转换)

server端:
1,socket() 创建socket
2,bind() 绑定服务器地址结构
3,listen() 设置监听上限
4,accept() 阻塞监听客户端连接
5,read(fd)读socket获取客户端数据
6,小写—>大写 toupper()
7,write(fd)
8,close()

client:
1,socket() 创建socket
2,connect() 与服务器建立连接
3,write() 写数据到socket
4,read() 读转换后的数据
5,显示读取的结果
6,close()