tcp协议和socket编程

tcp协议:

TCP 协议提供的是:面向连接、可靠的、字节流服务。使用 TCP 协议通信的双发必须
先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理
连接的状态和连接上数据的传输。TCP 连接是全双工的,双方的数据可以通过一个连接进行
读写。完成数据交换之后,通信双方都必须断开连接以释放系统资源。

特点

面向连接 可靠 流试服务 会产生粘包

可靠性:应答确认,超时重传 乱序重排 去重 滑动窗口 流量控制

tcp编程流程:

在这里插入图片描述
1.socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。
2.bind()方法是用来指定套接字使用的 IP 地址和端口。
3.listen()方法是用来创建监听队列。
4.accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。
5.send()方法用来向 TCP 连接的对端发送数据。
6.recv()方法用来接收 TCP 连接的对端发送来的数据。
7.close()方法用来关闭 TCP 连接。此时,会进行四次挥手。
8.connect()方法一般由客户端程序执行,需要指定连接的服务器端的 IP 地址和端口。

三次握手四次挥手:

connect()建立连接后进行三次握手,close()进行四次挥手。

三次握手:connect成功则证明三次握手成功。
第一次握手:客户端向服务器端发送连接请求。
第二次握手:服务器向客户端发生应发响应。
第三次握手:当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
三次握手可看做告白的过程。第一次握手是男孩说:“我们在一起吧”,然后女孩听到后开始第二次握手说:“好呀”。这个时候问题来了,男孩可能没有听到女孩的同意,此时开始第三次握手男孩说:“我们终于在一起了”来表示听到女孩的同意。

为什么要进行三次握手?
客户端先进行连接请求,服务器端收到请求回复应答,当客户端接收到应答响应时,就会进入estabished状态,而服务器端只有收到客户端的连接请求后才会进入estabished状态。
此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

在创建连接时也易遭到黑客恶意攻击,会一直发送连接请求,发送FIN,但不去管能否成功建立连接,会一直占用listen()的缓存区,使之无法成功建立链接。
在这里插入图片描述
四次挥手:第一条是通知关闭,第二条是回复确定。
在这里插入图片描述
四次挥手能不能能变成三次?
可以,当第一次close时发送FIN时,同时进行第二次close,可以把第一次close需要回复的ACK和第二次close发送的FIN一起发送。把四次挥手变为三次。
listen():两个,一个放置未完成握手的,一个放置完成握手的,

在这里插入图片描述
accept:处理完成握手的(c=accept)

线程实现多客户端链接

服务器端:

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<assert.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 #include<pthread.h>
 10 
 11 void* recv_thread(void* argv)
 12 {
 13     int c=(int)argv;
 14     while(1)
 15     {
 16         char buff[128]={0};
 17         int n=recv(c,buff,127,0);
 18         if(n<=0)
 19         {
 20             break;
 21         }
 22         printf("recv(%d):%s\n",c,buff);
 23         send(c,"ok",2,0);
 24     }
 25     printf("client close\n");
 26 }
 27 
 28 int main()
 29 {
 30     int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字,AF-INET是宏地址,SOCK-stream表示为tcp协议。
 31     assert(sockfd!=-1);
 32 
 33     struct sockaddr_in saddr, caddr;//ipv4的套接字地址结构,服务器端,客户端。
 34     memset(&saddr,0,sizeof(saddr));//初始化
 
```35     saddr.sin_family=AF_INET;
 36     saddr.sin_port=htons(6000);//端口号:主机服务器短整形:6000
 37     saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//ip地址
 38 
 39     int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定
 40     assert(res!=-1);
 41 
 42     res=listen(sockfd,5);
 43     assert(res!=-1);
 44 
 45     while(1)
 46     {
 47      int len =sizeof(caddr);
 48      int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
 49      if(c<0)
 50      {
 51            continue;
 52      }
 53 
 54      printf("accept c=%d\n",c);
 55     pthread_t id;
 56     pthread_create(&id,NULL,recv_thread,(void*)c);//创建线程
 57
 58 
 59     }
 60 close(sockfd);
 61 exit(0);
 62 }

客户端:

 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<assert.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 int main()
 10 {
 11    int  sockfd=socket(AF_INET,SOCK_STREAM,0);
 12    assert(sockfd!=-1);
 13 
 14    struct sockaddr_in saddr;
 15    saddr.sin_family=AF_INET;
 16    saddr.sin_port=htons(6000);
 17    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
 18 
 19    int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
 20    assert(res!=-1);
 21    while(1)
 22    {
 23      char buff[128]={0};
 24      printf("input:\n");
 25      fgets(buff,128,stdin);
 26      if(strncmp(buff,"end",3)==0)
 27      {
 28          break;
 29      }
 30      send(sockfd,buff,strlen(buff),0);
 31      memset(buff,0,128);
 32      recv(sockfd,buff,127,0);
 33      printf("buff=%s\n",buff);
 34     }
 35    close(sockfd);
 36 
 37    exit(0);
 38 }

执行结果:
在这里插入图片描述

进程实现多客户端链接(fork)

服务器端:

 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<assert.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 #include<pthread.h>
 10 #include<signal.h>
 11 #include<sys/wait.h>
 12 
 13 void fun(int sig)
 14 {
 15     wait(NULL);
 16 }
 17 int main()
 18 {
 19     signal(SIGCHLD,fun);//处理僵死进程
 20     int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字,AF-INET是宏地址,SOCK-stream表示为tcp协议。
 21     assert(sockfd!=-1);
 22 
 23     struct sockaddr_in saddr, caddr;//ipv4的套接字地址结构,服务器端,客户端。
 24     memset(&saddr,0,sizeof(saddr));
 25     saddr.sin_family=AF_INET;
 26     saddr.sin_port=htons(6000);//端口号:主机服务器短整形:6000
 27     saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//ip地址
 28 
 29     int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定
 30     assert(res!=-1);
 31 
 32     res=listen(sockfd,5);
 33     assert(res!=-1);
 31 
 32     res=listen(sockfd,5);
 33     assert(res!=-1);
 34 
 35     while(1)
 36     {
 37      int len =sizeof(caddr);
 38      int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
 39      if(c<0)
 40      {
 41            continue;
 42      }
 43 
 44      printf("accept c=%d\n",c);
 45 
 46      pid_t pid=fork();//创建子进程
 47      if(pid==-1)
 48     {
 49        printf("fork child err\n");
 50         close(c);
 51          continue;
 52      }
 53      if(pid==0)
 54      {
 55          while(1)
 56          {
 57              char buff[128]={0};
 58              int n=recv(c,buff,127,0);
59              if(n<=0)
 60              {
 61                  break;
 62              }
 63              printf("child read:%s\n",buff);
 64              send(c,"ok",2,0);
 65          }
 66          printf("client close\n");
 67          close(c);//关闭子进程
 68 
 69          exit(0);
 70      }
 71      close(c);//当产生子进程时,父进程可以关闭,去进行下一个循环
 72     }
 73 }

客户端:和线程相同

运行结果:

在这里插入图片描述

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值