TCP 通信流程

1.TCP通信过程

TCP 是一个面向连接的,安全的,流式传输协议,这个协议是一个传输层协议

(1)面向连接:是一个双向连接,通过三次握手完成,断开连接需要通过四次挥手完成;

(2)安全:tcp 通信过程中,会对发送的每一数据包都会进行校验,如果发现数据丢失,会自动重传;

(3)流式传输:发送端和接收端处理数据的速度,数据的量都可以不一致。

2.相关函数

(1)socket() 创建socket对象

(2)connect() 请求连接

(3)send() 发送消息

(4)recv() 接收

 (5)bind() 绑定ip和端口号

(6) listen() 监听是否有客户端的连接函数

(7)accept() 接收连接函数

(8)close()  通信结束关闭套接字函数

3.实现TCP通信的源码(含注释)

引入头文件:

#ifndef _NET_H
#define _NET_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#endif

客户端:

#include <stdio.h>
#include "net.h"

//编写一子函数,连接服务器端
//参数1:IP地址
//参数2:端口号
//返回值:连接成功的socket对象,失败则返回-1
int tcp_connect(const char * ip,int port)
{
	//1.创建TCPsocket对象  socket
	//参数1:协议 IPV4 AF_INET
	//参数2:流式套接字
	//参数3:总是0
	int tcp_socket=socket(AF_INET,SOCK_STREAM,0);
	if(tcp_socket<0)
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok\n");	
	//2.请求连接           connect
	struct sockaddr_in server;
	server.sin_family=AF_INET;//协议
	server.sin_port=htons(port);
	server.sin_addr.s_addr=inet_addr(ip);
	//参数1:打开的socket对象
	//参数2:服务器端的地址信息
	//参数3:大小 
	if(connect(tcp_socket,(struct sockaddr *)&server,sizeof(server))<0)
	{
		perror("connect error");
		return -1;
	}
	printf("connect ok\n");
	return tcp_socket;

}

//编写一子函数,实现tcp通信
//参数1:连接好的socket对象
//返回值:结束通信返回-1
int tcp_com(int sockfd)
{
	char buf[50]={'\0'};
	fgets(buf,sizeof(buf),stdin);
	send(sockfd,buf,strlen(buf),0);
	return -1;
}
int main(int argc, const char *argv[])
{
	
	if(argc<3)
	{
		printf("input app ip port\n");
		return -1;
	}
	
	//TCP通信客户端
	//1.创建TCPsocket对象  socket
	//2.请求连接           connect
	//3.发/收              read/write  send/recv
	//4.关闭               close
	//1.请求连接tcp服务器
	int sockfd=tcp_connect(argv[1],atoi(argv[2]));
	//2.tcp通信返回-1,表示通信结束
	int n=tcp_com(sockfd);
	if(n<0)
	{
		//关闭socket对象
		close(sockfd);
	}
	return 0;
}

服务器:

#include <stdio.h>
#include "net.h"

//编写一子函数,监听是否有人连接
//参数1:IP地址 char * ip
//参数2:端口号 int port
//返回值:成功返回监听的socket对象,失败返回-1
int tcp_server(const char * ip,int port)
{
	//1.创建tcpSocket对象  socket()
	int tcp_socket=socket(AF_INET,SOCK_STREAM,0);
	if(tcp_socket<0)
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok\n");
	//2.绑定自己的IP地址和端口号 bind()
	struct sockaddr_in myAddr;
	memset(&myAddr,'\0',sizeof(myAddr));
	myAddr.sin_family=AF_INET;//协议
	myAddr.sin_port=htons(port);
	myAddr.sin_addr.s_addr=inet_addr(ip);
	if(bind(tcp_socket,(struct sockaddr *)&myAddr,sizeof(myAddr))<0)
	{
		perror("bind error");
		return -1;
	}
	printf("bind ok\n");
	//3.监听(socket对象)是否有人连接 listen()
	if(listen(tcp_socket,5)<0)
	{
		perror("listen error");
		return -1;
	}
	printf("listen ok\n");
	//4.返回监听的socket对象
	return tcp_socket;
}

//编写一子函数,进行TCP通信
//参数:连接好的socket对象
//返回值:通信结束返回-1,
int tcp_com(int newfd)
{
	char buf[50]={'\0'};
	recv(newfd,buf,sizeof(buf),0);//接受第一条信息
	printf("%s\n",buf);

	return -1;
}




int main(int argc, const char *argv[])
{
	if(argc<3)
	{
		printf("input app ip port\n");
		return -1;
	}

	//argv[1]----->IP地址
	//argv[2]----->port
	//1.获得监听的socket对象
	int listenfd=tcp_server(argv[1],atoi(argv[2]));
	//2.接受连接
	int newfd=0; //连接好的socket对象
	//参数1:监听好的socket对象
	//可以通过参数2和3获得客户端的信息(IP/PORT),若不关心,先设置为NULL
	//newfd=accept(listenfd,NULL,NULL);
	//想获得对方的IP地址和端口号
	struct sockaddr_in client;
	memset(&client,'\0',sizeof(client));
	int len=sizeof(client);
	//参数2:struct sockaddr *
	//参数3:&len
	newfd=accept(listenfd,(struct sockaddr *)&client,&len);
	if(newfd<0)
	{
		perror("accpet error");
		return -1;
	}
	//client.sin_port    网络字节序 ----->主机字节序 ntohs(client.sin_port)
	//client.sin_addr.s_addr 网络字节序---->点分十进制的字符串  inet_ntoa(client.sin_addr)
	printf("accept ok client ip=%s port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

	//3.tcp通信
	//4.返回-1表示通信结束
	int n=tcp_com(newfd);
	//5.关闭socket对象
	if(n<0)
	{
		close(newfd);
	}

	close(listenfd);

	return 0;
}

// 以上内容为简单的TCP通信流程,其中的难点在于对函数参数的理解以及新生成的套接字newfd进行继续通信。读者在阅读时不妨设定这样一个场景:当我们在打电话通信时,第三个人给我们打电话(AB正在通信,C电话试图打通A),此时我们去进行三次握手后C和A进行正常通信,但是B可以保持接听状态,这就很好的模拟了TCP通信流程。

//关于三次握手、四次挥手的问题,比较复杂,读者可以查阅相关文档

-----------------------------------------------------------我是分割线---------------------------------------------------------end

 写在文末:本文主要对TCP通信的流程进行了讲解,对其中的函数参数及返回值进行说明,同时设定一个简单的打电话模拟场景去帮助我们了解通信的流程。源代码已经给到,希望能对读者有所帮助。博主目前在华清远见西安中心学习

/*原创内容,有任何问题欢迎私信*/

猜你喜欢

转载自blog.csdn.net/bx1091182836/article/details/127816642