【网络开发及应用--TCP协议】

TCP通信

套接字:

Socket将运输层的很多复杂操作封装成一些简单的接口,来让应用层调用,实现进程再网络中的通信
本质上是一个非负数,是一种特殊的文件描述符
创建套接字:int socket(int domain, int type, int protocol);
参数:
	domain:	协议族
		AF_UNX / AF_LOCAL	用于本地通信
		AF_INET			选择IPv4协议
		AF_INET6		选择IPv6协议
	type:	创建套接字类型
		SOCK_STREAM	流式套接字对应TCP与SCTP协议、数据传输时逐字节传输
		SOCK_DGRAM	数据报套接字对应UDP通信、数据传输时逐数据报传输(包含目的地址和其他数据)
		SOCK_SEQPACKET	有序分组套接字对应SCTP协议
		SOCK_RAM	原始套接字,跳过传输层
	protocol:
		0	表示默认方式
		IPPROTO_TCP、IPPROTO_UDR、IPPROTO_SCTP

返回值:返回的是编号最低的文件描述符,失败返回-1,并置error

创建服务器:

1、创建套接字	socket()
2、绑定本机IP地址与端口号		
		int bin(int sockfd, const struct sockaddr *addr, socklen_taddrlen)
		参数:
			sockfd: 监听的套接字
			addr:	对应协议服务器地址结构首地址:协议族选择、IP地址、port
			addrlen:地址结构的大小
3、设置监听套接字
    int listen(int sockfd, int backlog)
        参数:
            sockfd: 监听的套接字
            backlog:等待连接的最大队列长度
        返回值: 成功返回0,失败返回0,并置error
        
4、等待客户端连接
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
        参数:
            addr:       客户端的结构的首地址
            addrlen:    客户端的结构的大小的首地址
                    注:  若不想接收客户端的信息,addr和addrlen设置为NULL;   
        返回值:成功返回套接字,失败返回-1,并置ERROR
字节序:
5、数据收发
    read()/write()--recv()/send()
6、关闭套接字
    close();
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#define BUF_N 1024
#define LISTEN_MAX 5
#define PORT 8888
#define SERVER_IP "192.168.113.129"
#define DEF_VAL 0
#define ERR_VAL -1
#define ERR_LOG(val) do{perror(val);exit(EXIT_FAILURE);}while(0);

int main(int argc, const char * argv[])
{
	int ret = 0;

	//创建套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, DEF_VAL);
	if (ERR_VAL == sockfd)
	{
		ERR_LOG("socket");
	}

	printf("====create socket successful	sockfd: %d\n",sockfd);

	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(saddr));

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(PORT);
	saddr.sin_addr.s_addr = INADDR_ANY;

/*
	//字符串形式转换为网络字节序
	ret = inet_pton(AF_INET, SERVER_IP, &saddr.sin_addr);
	if (ERR_VAL == ret)
	{
		ERR_LOG("inet_pton");
	}
*/
	//绑定本机地址与端口号
	ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
	if (ERR_VAL == ret)
	{
		ERR_LOG("bind");
	}

	printf("====bind sucessful\n");

	//设置监听套接字
	ret = listen(sockfd, LISTEN_MAX);	//将套接字转换为被动套接字
	if (ERR_VAL == ret)
	{
		ERR_LOG("listen");
	}
	printf("====listen successful\n");

	printf("wait client connect...\n");

	//循环等待客户端连接
	while(1)
	{
		struct sockaddr_in caddr;
		memset(&caddr, 0, sizeof(caddr));
		socklen_t caddr_len = sizeof(caddr);

		//阻塞等待客户端连接、套接字必须为被动套接字,否则报错
		int connfd = accept(sockfd, (struct sockaddr *)&caddr, &caddr_len);	
		if (ERR_VAL == connfd)
		{
			ERR_LOG("accept");
		}
		
		char dst[20] = {0};


		printf("====accept successful %s : %d\n",
				inet_ntop(AF_INET, &caddr.sin_addr, dst, sizeof(dst)),						ntohs(caddr.sin_port));
		

		char buf[BUF_N] = {0};
	
		//数据发送与接收
		while(1)
		{
			int len = read(connfd, buf, sizeof(buf));
			if (len <= DEF_VAL)
			{
				printf("client exit...\n");
				len = 1;
				break;
			}
			write(connfd, buf, strlen(buf));
			printf("client %s : %d send massage: %s", 
					dst, ntohs(caddr.sin_port), buf);
			memset(buf, 0, sizeof(buf));
		}
	
		close(connfd);
	}
	close(sockfd);


	return 0;
}

创建客户端:

1、创建套接字
2、绑定本机IP地址与端口号(可以省略,内核会做)
3、向服务器发起连接
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
        参数:
            addr:服务器地址结构的首地址
            注:connect调用失败必须关闭套接字,不能重复使用
        返回值: 成功返回0,失败返回-1,并置error
4、数据收发:
    read()/write()--recv()/send()
5、关闭套接字
    close()
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#define BUF_N 1024
#define DEF_VAL 0
#define ERR_VAL -1
#define ERR_LOG(val) do{perror(val);exit(EXIT_FAILURE);}while(0);

int main(int argc, const char * argv[])
{
	int ret = 0;

	if (argc != 3)
	{
		fprintf(stderr, "Usage %s filename\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	int port = atoi(argv[2]);

	//创建套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, DEF_VAL);
	if (ERR_VAL == sockfd)
	{
		ERR_LOG("socket");
	}

	printf("====create socket successful	sockfd: %d\n",sockfd);

	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(saddr));

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	ret = inet_pton(AF_INET, argv[1] ,&saddr.sin_addr);
	if (ERR_VAL == ret)
	{
		ERR_LOG("inet_pton");
	}

	//向服务器发起连接
	ret = connect(sockfd, (const struct sockaddr *) &saddr, sizeof(saddr));
	if (ERR_VAL == ret)
	{
		close(sockfd);
		ERR_LOG("connect");
	}
	printf("====connect successful\n");


	char buf[BUF_N] = {0};
	//数据发送接收
	while(1)
	{
		int len = read(STDIN_FILENO, buf, BUF_N);
		ret = send(sockfd, buf, len, 0);
		if (ERR_VAL == ret)
		{
			ERR_LOG("send");
		}
		memset(buf, 0, sizeof(buf));

		ret = recv(sockfd, buf, len, 0);
		if (ERR_VAL == ret)
		{
			ERR_LOG("recv");
		}
		printf("from server massage: %s", buf);
		memset(buf, 0, sizeof(buf));

	}

	close(sockfd);
	return 0}

字节序

大端字节序:低数据存在高位(ARM)、网络字节序
小段字节序:低位数据存在低地址位(inter芯片)、本机字节序
大小端字节序在内存中存放数据有差异

    代码判断大小端:
        int a = 0x12345678;
        short *p = (short *)&a;
        if (*p == 0x78)
        {
            printf("小段\n");
        }
        else
        {
            printf("大端\n");
        }

TCP通信特点:

1、点对点的连接(双方的套接字只能相互间接收和发送、双方具有固定的IP和port)
2、保证数据准确性、内容、时许无误
3、无丢失、无重复、无差错、按时到达(TCP出错机制保证)
TCP出错机制:TCP向另一端传送数据时,要求对面返回一个确认,如果没有确认,TCP就重传并等待更长时间,数次失败后放弃;

三次握手:

在这里插入图片描述

第一次:客户端应用程序主动开启,并向服务端发出请求报文段(握手钱服务器需处于LISTEN状态),(此后客户端处于SYN_SENT状态)
第二次:服务器应用进程被打开,发回确认报文,(此后服务器处于SYN_RCVD状态)
第三次:客户端接收到确认报文后,通知应用进程连接已建立,(客户端处于ESTABLISHEN状态)并向服务器发送报文,当服务器收到客户端的确认报文之后,也通知其上层应用进程连接已建立(服务器处于ESTABLISHED状态)、客户端在第三次握手时就可以向服务器发送数据了;

目的:确认客户端和服务器可以正常通信

四次挥手

在这里插入图片描述

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭;
请求断开的可以是服务器也可以是客户端,但大多时候都是客户端;
客户端为主动方:
第一次:TCP客户端主动发送一个FIN,用来关闭客户到服务器的数据传送;
第二次:服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1;
第三次:服务器关闭客户端的连接,发送一个FIN给客户端;
若服务器没有需要处理的数据,第二次和第三次可以合并为一次;
第四次:客户端发回ACK报文确认,并将确认序号设置为收到序号加1;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳请了个假

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值