TCP/IP :
TCP/IP:在网络通信中,TCP/IP是主流协议()
应用层:用户自定义的协议(HTTP,EMAIL,),用于用户之间数据的传送
传输层:(传输控制:TCP,UDP)负责点对点之间连接建立,传输控制协议的指定()
网络层:用于查找路由(查找)
网络接口层 : 将二进制转换为数据帧。
TCP帧:
[源端号][目的端口]
[ 顺序号 ]
[TCP包头长][URG/ACK/PSH/RST/SYN/FIN][窗口大小]
[检验和][紧急指针]
[可选项 ]
[数据包]
TCP:面向连接,安全可靠有状态的传输协议。(比UDP效率差)
怎样连接:三次握手简述(确保双方一定同时在线)
A与B建立TCP连接时:首先A向B发SYN(同步请求)
然后B回复SYN+ACK(同步请求应答)
最后A回复ACK确认
这样TCP的一次连接(三次握手)的过程就建立了!
/**** 下面部分转载自 : https://blog.csdn.net/sssnmnmjmf/article/details/68486261
TCP握手协议详述 :
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接.
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
SYN:同步序列编号(Synchronize Sequence Numbers)
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手.
完成三次握手,客户端与服务器开始传送数据
所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。 (2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。 (3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV
上面转载到此处结束 ****/
TCP通信:
服务器:
1、创建套接字(打开设备)
socket
2、绑定
bind
3、监听:(设置最大的可连接的线路的数量)
listen
4、等待连接:是一个阻塞函数(注:执行一该函数,则连接一次)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回一个套接字,用于在此套接字上收发消息
5、收到消息
recv
6、关闭套接字 注:由于accept和recv都需要阻塞等待,accept返回新的连接描述符。
为每一个新的连接请求分配一个新的进程或线程
客户端:
1、创建套接字(打开设备)
2、绑定套接字
3、发出连接请求
connect
4、收发消息
send
recv
5、关闭请求
close
传输层:UPD报文SOCK_DGRAM TCP:流式套接字SOCK_STREAM
注:由于套接字上绑定有端口号,为了防止复用端口号,套接字默认不允许复用。
API:设备套接字 ----> setsockopt ( int sock,int level ) ; level:SOL_SOCKET 对套接字进行设置 socklen_t len=1 ;
if ( setsockopt ( sock,SOL_SOCKET,SO_REUSEADDR,&len,sizeof(len) ) <0 )
{
perror ( "setsocket fail" ) ;
return -1 ;
}
TCP 服务器的代码实现 :
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
//创建锁
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
//子线程:接收链路信息
void* threadfun(void* arg)
{
int sock=*(int*)arg;
//解锁
pthread_mutex_unlock(&mutex);
//接收消息
char buf[100]="";
int ilen=0;
while(1)
{
ilen=recv(sock,buf,99,0);
if(ilen<=0)
break;
buf[ilen]='\0';
printf("收到:%s\n",buf);
}
close(sock);
}
//TCP:流式套接字
int main()
{
//1创建套接字---打开设备
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket fail");
return -1;
}
//修改套接字的网络层:允许IP复用
socklen_t len=1;
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&len,sizeof(len))<0)
{
perror("setsocket fail");
return -1;
}
//2填充结构体并绑定
struct sockaddr_in myaddr; //7979;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family =AF_INET;
myaddr.sin_port =htons(7979);
myaddr.sin_addr.s_addr =INADDR_ANY;
if(bind(sock,(struct sockaddr*)&myaddr,sizeof(myaddr))==-1)
{
perror("bind fail");
return -1;
}
//3监听:设置最大的可连接数量,并监听
listen(sock,44);
//4等待连接 accept
int newsock=-1;
pthread_t tid;
while(1)
{
//上锁
pthread_mutex_lock(&mutex);
newsock=accept(sock,NULL,NULL);
if(-1==newsock)
break;
//创建线程--函数
pthread_create(&tid,NULL,threadfun,&newsock);
//cout<<"有人连接我了\n"<<endl;
}
/*
//5等待该链路上消息到来
char buf[100]="";
recv(newsock,buf,99,0);
printf("收到:%s\n",buf);
*/
//6关闭
close(sock);
return 0;
}
TCP 客户端的代码实现 :
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<string.h>
int main()
{
//1创建套接字
int sock=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM 流氏sock
//2[绑定:系统为该套接字默认绑定一个空的端口号和一个网卡地址]
//3主动发出连接请求
/*填充连接服务器的IP信息*/
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr)); //填充为0
saddr.sin_family =AF_INET;
saddr.sin_port =htons(7979);
saddr.sin_addr.s_addr =inet_addr("192.168.8.209");
if(connect(sock,(struct sockaddr*)&saddr,sizeof(saddr))<0) //连接
{
perror("connect fail");
return -1;
}
//4收发消息
char buf[100]="";
while(1)
{
scanf("%s",buf); //客户端可以连续的发送内容
send(sock,buf,strlen(buf),0);//strlen 求字符串的长度
}
//5关闭
close(sock);
}