网络基础以及基础编程-TCP

本文介绍了TCP/IP模型及网络编程的基础知识,详细解释了TCP协议的工作原理,包括三次握手建立连接、四次挥手释放连接的过程,并通过实例展示了服务器端与客户端的编程流程。

前面的博客关于Linux的都是操作系统方面以及系统编程,从这篇开始,我们开始介绍了解学习一下关于网络方面的基础知识以及网络编程。我们既然要开始网络编程,那么学习了解网络的基础知识是必须的。

什么是网络?网络就是将主机连接起来就可称之为网络。这样主机与主机之间就可以进行通讯了,那么在网络通信过程中的主角是什么?实质是进程和进程之间的通信。要进行通信那么就要知道它的IP地址和端口号,我们平时说的网卡地址就是MAC地址,如果网卡不更换,那么MAC地址是不变的。

网络层次划分:OSI模型和TCP/IP

OSI 模型:

应用层 表示层 会话层 传输层 网络层 数据链路层 物理层

TCP/IP:

应用层 传输层 网络层 数据链路层

各层主要用的协议:

传输层: tcp udp 端到端的通信

网络层: ip 选路存储转发

应用层: http  ftp


提供数据的一方叫做服务器端,获取数据的一方叫做客户端。

传输层协议主要有两个:TCP协议和UDP协议:

TCP:面向连接   可靠的         流式服务

UDP:无连接      不可靠的      数据报服务

使用TCP协议通信的双方必须先建立连接,才能开始数据的读写,TCP协议是全双工的,即双方的数据读写可以通过一个连接进行。TCP协议的这种连接是一对一的,所以基于广播和多播的应用程序不能使用TCP,而无连接协议UDP则非常适合。

TCP是面向连接的可靠的流式服务,下面我们来看看这些的具体体现。

TCP报头:


16位源端口号:告诉主机报文是由谁发送来的。

16位目的端口号:告诉主机需要把报文段传给哪一个上层协议或应用程序。

32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号,确保报文段都是整齐有序的。

32位确认号:用作对另一方发送来的TCP报文段的相应,其值是收到的TCP报文段的序号加1。

4位头部长度:标识该TCP头部有多少个32bit字(4字节)。因为4位最大能表示15,所以TCP头部最长是60字节。

6位标志位:

  URG标志,表示紧急指针是否有效。

  ACK标志,表示确认号是否有效。我们称携带ACK标志的TCP报文段为确认报文段。

  PSH标志,提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间。

  RST标志,表示要求对方重新建立连接。

  SYN标志,表示请求建立一个连接。

  FIN标志,表示通知对方本段要关闭连接了。

16位窗口大小:是TCP流量控制的一个手段。告诉对方我还能接收多大的数据,这样对方就可以控制着发送,不至于过大本端接受不了。

16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法用来校验TCP报文段在传输过程中是否损坏。

16位紧急指针:是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一个字节的序号。

1、面向连接

三次握手建立链接


客户端向服务器发送SYN请求链接,服务器收到后向客户端发送SYN、ACK意思就是我收到了你的请求并且可以连接我的确认,客户端收到后再向服务器发送ACK确认,然后两者就建立起链接。建立连接过程至少进行三次,不能少于三次,否则链接失败。

四次挥手释放链接


客户端向服务器发送断开链接的请求,服务器收到后向客户端发送确认ACK,之后一段时间服务器会有一段时间来处理数据,完成后服务器会向客户端发送FIN关闭连接,客户端收到后向服务器发送ACK确认,这样链接就断开了。四次挥手也可以是三次,假如服务器不需要多余的时间来处理数据的话,三次挥手即可断开链接。

三次握手和四次挥手状态图


TIME_WAIT状态:出现在主动发起断开链接请求的一端

      意义:1、保证可靠的终止TCP链接

                 2、保证迟来的数据报能被识别并丢弃

2、可靠的

(1)32位序号--保证数据有序。

(2)32位确认号和超时重传--保证报文段不会丢失。

(3)16位窗口大小--保证数据可以被全部接收不丢失。

(4)首部校验和--保证数据准确,不失真。

3、流式服务(数据流)


发送端可能会发送多次数据到发送缓冲区中,接收端从接受缓冲区中接收数据可能一次性就可以将全部的数据接受,发送端发送的次数与接收端接受的次数没有关系。

编程流程:

服务器端(server):socket  bind listen  accept  recv/send close

客户端(client):socket  /*bind*/ connect  recv/send  close

函数原型:

int  socket(int domain, int type, int protocol);

domain::协议簇  AF_INET

type::选择协议 SOCK_STREAM(TCP)   SOCK_DGRAM(UDP)

protocol::0

int bind(int sockfd, struct sockaddr *addr, int addrlen);

addr::指定IP地址和端口号

struct sockaddr_in

{

     sa_family_t  sin_family;//地址簇  AF_INET

     u_int16_t  sin_port;//端口号 

//网络字节序(大端模式)PC机(小端模式)

      struct in_addr  sin_addr;//IP地址

}

struct in_addr

{

    u_int32_t  s_addr;//IP地址最终的表示

}

int  listen(int sockfd, int size);// 创建一个监听队列
int  accept(int sockfd, struct sockaddr *addr, int *addrlen);

//会阻塞,当队列中没有链接时,等待链接,有了就取出

      返回值:获取到的和客户端链接的文件描述符

      addr::要连接的服务器的IP地址和端口号

int connect(int sockfd, struct sockaddr *addr, int *addrlen);

addr::要连接的服务器的IP地址和端口号

ser.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

void main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);

	struct sockaddr_in ser,cli;
	ser.sin_family=AF_INET;//di zhi zu
	ser.sin_port=htons(6000);//duan kou hao  host  to  net  short
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");

	int res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	assert(res!=-1);

	listen(sockfd,5);

	while(1)
	{
		int len=sizeof(cli);
		int c=accept(sockfd,(struct sockaddr*)&cli,&len);
		assert(c!=-1);
	
		char buff[128]={0};
		recv(c,buff,127,0);
		printf("recv::%s\n",buff);
		send(c,"I Know",sizeof("I Know"),0);

		close(c);
    }
	close(sockfd);

}
cli.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

void main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);

	struct sockaddr_in ser,cli;
	ser.sin_family=AF_INET;
	ser.sin_port=htons(6000);
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");

	int res=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	assert(res!=-1);

	send(sockfd,"hello world",strlen("hello world"),0);
	char buff[128]={0};
	recv(sockfd,buff,127,0);

	printf("recv::%s\n",buff);

	close(sockfd);
}

运行时先运行服务器后运行客户端

断言时崩溃的原因:

1、IP地址不对

2、端口号不对(端口号可能正在被使用或者没有权限)













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值