套接字的文件描述符
监听的文件描述符
客户端发送连接请求,写到 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 }
要把前面的数字去掉
编译运行
启动服务端
第二个窗口启动客户端连接
此时客户端 连接服务器成功
服务器端 也链接成功
关闭服务器
此时客户端显示