初见成效!!TCP连接成功(最易懂)

套接字的文件描述符

监听的文件描述符

客户端发送连接请求,写到 writebuffer 里面,然后cfd 链接监听的文件描述符 lfd 的readbuffer里面

通信的文件描述符

调用发送数据的函数,并没有发送出去,而是到了内核的写缓冲区里面,内核检测到写缓冲有数据,会将数据发送到网络的另外一端

如果是通信,就会进入对方的读缓冲区,双方都是通信套接字,然后对方调用一个read函数,就可以取出

write();

套接字函数

服务器端有监听和通信的套接字,客户端只有通信的

1.创建套接字(文件描述符)用于监听或者通信都可以

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

参数:

domain :

AF_INET:使用ipv4 网络协议

AF_INET6:使用ipv6的网络协议

type:

SOCK_STREAM :使用流式传输协议

SOCK_DGRAM:使用报式传输协议

protocol:默认为0

流式协议 0是tcp

报式协议 默认使用udp

返回值:

成功:返回一个文件描述符

失败:返回-1;

2.将监听的套接字和本地ip端口进行关联

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

参数:

sockfd:用于监听的套接字,通过socket函数创建的

addr :将本地的ip和端口信息初始化给该结构体,ip和端口使用大端

绑定的时候服务器端一般使用 INADDR_ANY==0;

0==0.0.0.0

表示绑定

addrlen :记录第二个参数指针指向的内存大小,sizeof()

返回值:

成功返回0;

失败返回-1;

3.给监听的套接字设置监听,开始检测客户端链接

int listen(int sockfd ,int backlog);

参数:

sockfd :监听的套接字,设置监听前要bind()绑定

backlog :可以同时检测的新的连接个数,最大值128,一轮一轮的128,写死的在内核中

返回值:

成功返回0

失败 -1

4.等待并接受客户端连接,
本身是阻塞函数,没有连接就阻塞,
监听的文件描述符读缓冲区没有数据就阻塞,如果有就接触阻塞→连接建立 ,建立成功返回一个通信的文件描述符

int accept(int sockfd ,struct sockaddr *addr, socklen_t* addrlen);

参数:

sockfd :监听的文件描述符

addr :传出参数,保存了建立连接的客户端的地址信息(ip+端口)→大端存储,到本地需要转到小端

如果不需要客户端信息:addr 可以写null 但是addrlen和addr必须一致

addrlen :传入传出,传入addr指针指向内存的大小,传出储存了客户端信息的addr内存大小

返回值: 文件描述符

5.接受数据 (客户端,服务端都会用,读缓冲为空阻塞)

ssize_t read(int sockfd,void *buf,size_t size);

ssize_t recv(int sockfd, void *buf, size_t size, int flags);

参数:

sockfd :通信的文件描述符

 accept的返回值(服务器端,会返回通信文件描述符 ) 

通过socket函数创建的,通过客户端connect初始化才能连接的

buf :储存接收数据,数据来自通信的文件描述符对应的读缓冲区

size :buf对应的内存容量

flag :默认写0;

返回值:

0:读到的字节数

=0:对方已经断开了连接

-1 :读异常

6.发送数据(写缓冲满了,阻塞)

ssize_t write(int fd, const void *buf, size_t len);

ssize_t send(int fd ,const void *buf, size_t len, int flags);

参数:

fd :通信的文件描述符

accept 的返回值 服务器端

客户端(通过socket函数创建,通过connect初始化连接

buf :要发送的数据,数据进入到了通信的文件描述符的写缓冲区

写缓冲区有内核维护,有数据就会被内核发送

len :发送的数据实际长度 ,strlen();

flag :使用默认属性,指定为0即可;

返回值:

0:发送的字节数

=0:没有发送任何数

-1:发送失败

7.客户端用连接服务器

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

参数 :

sockfd :通信的文件描述符,通过socket

add:连接的服务器的ip和端口信息,初始化该变量中

addlen :参数addr指向的内存大小

返回值:

成功 0;

失败 -1;

程序实现

首先配备server.cpp  和client.cpp 服务器端和客户端

server.cpp 示例代码
   8#include <iostream>
  9 #include <stdio.h>
 10 #include <cstdlib>
 11 #include <arpa/inet.h>
 12 #include <unistd.h>
 13 #include <algorithm>
 14 #include <cstring>
 15 #include <map>
 16 #include <vector>
 17 using namespace std;
 18 int main(){
 19 
 20     // 1.创建监听的套接字
 21  int lfd=socket(AF_INET,SOCK_STREAM,0);
 22  if(lfd==-1){
 23  perror("socket");
 24  exit(0);
 25  }
 26  //2.绑定
 27  struct sockaddr_in addr;
 28  addr.sin_family =AF_INET;  //IPV4
 29  addr.sin_port=htons(8989);//端口,转换大端
 30  addr.sin_addr.s_addr=INADDR_ANY;//ip地址为0
 31  int ret =bind(lfd,(struct sockaddr*)&addr,sizeof(addr));//绑定
 32 if(ret==-1){//如果绑定失败
 33 perror("bind");
 34 exit(0);
 35 }
// 3. 设置监听 
 37 ret =listen(lfd,128);
 38 if(ret==-1){
 39 
 40     perror("listen");                                                                  
 41     exit(0);
 42 }
 43 //4. 等待客户端链接
 44 struct sockaddr_in cliaddr;
 45 socklen_t clilen =sizeof(cliaddr);
 46 int cfd =accept(lfd,(struct sockaddr*)&cliaddr,&clilen);//读入读出相同,//客户端
 47 if(cfd==-1){
 48 perror("accept");
 49 exit(0);
 50 }
 51 
 52 // 5. 通信
 53 while (1){
 54  //接受数据
 55  char buf[1024];
 56  memset(buf,0,sizeof(buf));
 57 int len= recv(cfd,buf,sizeof(buf),0);
 58  if(len==0){
 59 cout<<"客户端断开连接"<<endl;
 60 break;
 }
 62 else if(len>0){
 63 printf("recv buf: %s\n",buf);
 64 send(cfd,buf ,strlen(buf)+1,0);
 65 }
 66 else{
 67 perror("recv");
 68 break;
 69 }
 70 
 71 
 72 }
 73 close(cfd);
 74 close(lfd);
 75 
 76 
 77 
 78 
 79     return 0;
 80 }

                          
client.cpp 程序代码

  8 #include <iostream>                                                                    
  9 #include <cstdio>
 10 #include <cstdlib>
 11 #include <arpa/inet.h>
 12 #include <unistd.h>
 13 #include <algorithm>
 14 #include <cstring>
 15 #include <map>
 16 #include <vector>
 17 using namespace std;
 18 int main(){
 19 
 20     // 1.创建通信的套接字
 21  int cfd=socket(AF_INET,SOCK_STREAM,0);
 22  if(cfd==-1){
 23  perror("socket");
 24  exit(0);
 25  }
 26 
 27  //2.connect连接
 28  struct sockaddr_in addr;
 29  addr.sin_family =AF_INET;  //IPV4
 30  addr.sin_port=htons(8989);//端口,转换大端
 31  //将192.168.88.130 ->大端整型
 32  inet_pton(AF_INET,"192.168.88.130",&addr.sin_addr.s_addr);
 33  int ret =connect(cfd,(struct sockaddr*)&addr,sizeof(addr));//绑定
 34 if(ret==-1){//如果绑定失败
 35 perror("connect");
 36 exit(0);
 37 }
//3. 通信                                                                              
 41  int num=0;
 42 while (1){
 43  //发送数据
 44  char buf[1024];
 45   sprintf(buf,"hello,world,%d,.......",num++);
 46   send(cfd,buf,strlen(buf)+1,0);
 47   //接受数据
 48   memset(buf,0,sizeof(buf));
 49 int len= recv(cfd,buf,sizeof(buf),0);
 50  if(len==0){
 51 cout<<"服务器断开连接"<<endl;
 52 break;
 53  }
 54 else if(len>0){
 55 printf("recv buf: %s\n",buf);
 56 }
 57 else{
 58 perror("recv");
 59 break;
 60 }
 61 
 62 sleep(2);
 63 
 64 }
 65 // 4.断开链接
 66 close(cfd);
 67 
 68 
 69 
 70 
 71     return 0;
 72 }

要把前面的数字去掉

编译运行

启动服务端

第二个窗口启动客户端连接

此时客户端 连接服务器成功

服务器端 也链接成功

关闭服务器

此时客户端显示

完成!
  • 33
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值