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
#include
#include
#include
#include
#include
//创建锁
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"<
TCP 客户端的代码实现 :
#include
#include
#include
#include
#include
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);
}
TCP 的服务器 , 实现从客户端发送信息到服务器 , 服务器接收到文件名后 , 将服务器的文件发送给客户端 , 从而实现客户端下载服务器的内容 , 如下服务器代码 :
#include
#include
#include
#include
#include
#include
#include
//创建锁
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)
{
char ch[100]="";
char chr[1024]="";
ilen=recv(sock,buf,99,0);
if(ilen<=0)
break;
buf[ilen]='\0';
printf("即将copy文件名%s:",buf); //buf的内容为客户端发送过来的文件名
int fdr=open(buf,O_RDONLY);
if(-1==fdr)
{
perror("fail:");
return ;
}
while((ilen=read(fdr,chr,1024))>0) //将文件读取到 chr[1024] 中
{
send(sock,chr,strlen(chr),0); //发送内容给客户端
}
}
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"<
客户端的实现 :
#include
#include
#include
#include
#include
#include
int main()
{
//1创建套接字
int sock=socket(AF_INET,SOCK_STREAM,0);
//2[绑定:系统为该套接字默认绑定一个空的端口号和一个网卡地址]
//3主动发出连接请求
/*填充连接服务器的IP信息*/
//int pid=fork();
char buf[100]="";
//if(pid>0)
//{
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收发消息
while(1)
{
char bbb[100]="";
printf("输入需要拷贝的文件:"); //输入服务器的目录下的文件名
scanf("%s",bbb);
send(sock,bbb,strlen(bbb),0);//strlen 求字符串的长度 , 将文件名发送给服务器
break;
}
int ilen=0;
char aaa[100]="";
printf("拷贝为:");
scanf("%s",aaa); //将需要拷贝的文件名命名为 XXX
int fdw=open(aaa,O_WRONLY|O_CREAT|O_TRUNC,0644);
while(1)
{
ilen=recv(sock,buf,99,0);
if(ilen<=0)
break;
buf[ilen]='\0';
write(fdw,buf,ilen); //写入到文件里面
}
//5关闭
close(sock);
}
下面是 TCP 的从客户端发送文件名到服务器 , 服务器将文件发送给客户端 , 改进后的代码 , 更加精炼 , 反正比我上面写的好 ,
下面是服务器端的代码 :
#include
#include
#include
#include
#include
#include
#include
#include
//拷贝文件信息
struct fileMsg
{
char path[255]; //文件名
int sock; //套接字
};
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
//线程函数
void* threadfun(void* arg)
{
//detach线程:当子线程结束,自己在结束之前释放空间,不必需要让父进程来通过join释放
pthread_detach(pthread_self());//线程结束,会结束此空间
//取出套接字
struct fileMsg fm=*(struct fileMsg*)arg;
pthread_mutex_unlock(&mutex);
//一边读取文件 一边发送信息
char buf[1024]="";
int ilen=-1;
int fd=open(fm.path,O_RDONLY);
if(fd<0)
{
perror("open fail\n");
}
else
{
while((ilen=read(fd,buf,1024))>0)
{
if(send(fm.sock,buf,ilen,0)!=ilen)
break;
}
}
close(fm.sock);
close(fd);
}
int main(int argc,char* argv[])
{
if(argc<2)
{
perror("arg 不足\n");
return -1;
}
//判断文件的权限是否存在---读
if(access(argv[1],R_OK|F_OK)==-1)
{
fprintf(stderr,"%s %s\n",argv[1],strerror(errno));
}
//填充结构体:
struct fileMsg fm;
strcpy(fm.path,argv[1]);
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("sock");
return -1;
}
//修改套接字的属性--允许复用
socklen_t s1=sizeof(socklen_t);
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&s1,sizeof(s1))<0)
{
perror("setsock fail");
return -1;
}
struct sockaddr_in myaddr;
bzero(&myaddr,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))<0)
{
perror("bind fail");
return -1;
}
if(listen(sock,44)<0)
{
perror("listen fail");
return -1;
}
int newsock=0;
pthread_t tid;
while(1)
{
//上锁
pthread_mutex_lock(&mutex);
newsock=accept(sock,NULL,NULL);
if(-1==newsock)
{
break;
}
//填充结构体
fm.sock=newsock;
//多位同学连接,创建线程
if(0!=pthread_create(&tid,NULL,threadfun,&fm))
{
//解锁
pthread_mutex_unlock(&mutex);
//关闭套接字
close(newsock);
continue;
}
}
close(sock);
return 0;
}
客户端的代码 :
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char* argv[])
{
if(argc<2)
{
perror("参数不足");
return -1;
}
int fd=open(argv[1],O_WRONLY|O_TRUNC|O_CREAT,0644);
if(fd<0)
{
perror("open fail");
return -1;
}
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket fail");
}
//发送连接请求--3次
struct sockaddr_in saddr;
saddr.sin_family =AF_INET;
saddr.sin_port =htons(7979);
saddr.sin_addr.s_addr =inet_addr("192.168.8.115");
if(connect(sock,(struct sockaddr*)&saddr,sizeof(saddr))<0)
{
perror("connect fail\n");
return -1;
}
//接收
int ilen;
char buf[1024]="";
//从网络上拷贝
while((ilen=recv(sock,buf,1024,0))>0)
{
//写入到文件中
if(write(fd,buf,ilen)!=ilen)
{
perror("write fail");
break;
}
}
close(sock);
close(fd);
}
雨落下雾茫茫,问天涯在何方 《雨下一整晚》
你可能感兴趣的:(♬,--,C\C++)