回射客户-服务器模型(1)
2017-02-23 23:55
197 查看
最近在学习socket编程,根据自己的学习过程及学习笔记,下面来梳理一下如何实现一个简单的回射客户-服务器模型,也借此来熟悉一下socket、bind、listen、accept、connect这些函数的使用。
简单的回射客户/服务器模型
下面先看一下一个客户/服务器模型的框架图。
可以看到,服务器创建过程一般是:
1)创建套接字,使用socket函数,这个时候的套接字是主动套接字;
2)初始化服务器端地址,并使用bind函数将套接口与该地址进行绑定;
3)监听,使用listen函数,将套接口从close状态转为监听状态才能够接收客户端发起的连接,同时经过监听之后,服务器端的套接字从主动状态(发起连接)变为被动状态(接收连接);
4)等待接收连接,accept函数,使用该函数后,服务器端一直处于阻塞状态,直到客户端的连接到达;
5)等客户端的连接到达后,双方开始进行通信(读写数据);
客户端创建的过程就相对简单了:
1)创建套接字;
2)使用connect函数发起连接(过程中要经过三次握手);
3)连接成功,双方相互进行通信;
下面以代码的方式具体说明每一步骤的实现。
服务器端:
需要对上述的一些步骤做一些说明:
1)在初始化地址的时候,服务器端地址可以指定为具体某一IP,也可以是INADDR_ANY。这里建议选用INADDR_ANY参数,表示地址0.0.0.0,也就是不确定的地址,或“所有地址”、“任意地址”。如果你的服务器有多个网卡,你要在5188这个端口上监听,所有发送到服务器的这个端口,不管是哪个网卡/哪个IP地址接收到的数据,都可以进行处理。
2)关于listen函数中的第二个参数。每一个处于监听状态的端口,都要自己的监听队列,那么该参数就指定了监听队列的长度。我们可以自己指定一个正整数,也可以使用参数SOMANCONN,它表示每个端口的最大监听队列长度。
客户端:
简单的回射客户/服务器模型
下面先看一下一个客户/服务器模型的框架图。
可以看到,服务器创建过程一般是:
1)创建套接字,使用socket函数,这个时候的套接字是主动套接字;
2)初始化服务器端地址,并使用bind函数将套接口与该地址进行绑定;
3)监听,使用listen函数,将套接口从close状态转为监听状态才能够接收客户端发起的连接,同时经过监听之后,服务器端的套接字从主动状态(发起连接)变为被动状态(接收连接);
4)等待接收连接,accept函数,使用该函数后,服务器端一直处于阻塞状态,直到客户端的连接到达;
5)等客户端的连接到达后,双方开始进行通信(读写数据);
客户端创建的过程就相对简单了:
1)创建套接字;
2)使用connect函数发起连接(过程中要经过三次握手);
3)连接成功,双方相互进行通信;
下面以代码的方式具体说明每一步骤的实现。
服务器端:
// 1. 创建套接字 int listenfd; if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("socket create error"); // 2. 初始化地址 struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET;//地址族 servaddr.sin_port = htons(5188); //端口 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);// 表示本机的任意地址,当然也可以自己指定 //servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 3. 绑定bind if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) perror("bind error"); // 4. 监听listen if(listen(listenfd, SOMAXCONN) < 0)//第二个参数表示每一个端口的最大监听队列长度 perror("listen error"); // 5. 接收连接 // 在接收连接前,先定义一个地址结构,用来保存对等方的地址 struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); memset(&peeraddr, 0, peerlen); int conn; // 成功返回对等方的套接口 if((conn = accept(lisenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) perror("accept error"); //输出客户端的地址和端口(仅测试用) printf("IP=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); // 至此,双方连接已完成,可以进行通信 char recvbuf[1024]; while(1) { memset(recvbuf, 0, sizeof(recvbuf)); int ret = read(conn, recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout); //再回射给客户端 write(conn, recvbuf, ret); } //关闭套接口 close(lisenfd); close(conn);
需要对上述的一些步骤做一些说明:
1)在初始化地址的时候,服务器端地址可以指定为具体某一IP,也可以是INADDR_ANY。这里建议选用INADDR_ANY参数,表示地址0.0.0.0,也就是不确定的地址,或“所有地址”、“任意地址”。如果你的服务器有多个网卡,你要在5188这个端口上监听,所有发送到服务器的这个端口,不管是哪个网卡/哪个IP地址接收到的数据,都可以进行处理。
2)关于listen函数中的第二个参数。每一个处于监听状态的端口,都要自己的监听队列,那么该参数就指定了监听队列的长度。我们可以自己指定一个正整数,也可以使用参数SOMANCONN,它表示每个端口的最大监听队列长度。
客户端:
// 1. 创建套接字 int sock; if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("socket create error"); // 2. 初始化一个你要连接的对方的地址 struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 3. 发起连接 if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) perror("connect error"); // 4. 进行通信(读写数据) char recvbuf[1024] = {0}; char sendbuf[1024] = {0}; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { //发送数据(请求连接) write(sock, sendbuf, strlen(sendbuf)); //读取服务器端回射来的数据 read(sock, recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout); //每次读取完毕需要清空缓冲区 memset(sendbuf, 0, sizeof(sendbuf)); memset(recvbuf, 0, sizeof(recvbuf)); } //关闭套接口 close(sock);
相关文章推荐
- SELECT 模型,多客户单服务器.
- Unix/Linux网络编程(1)——简单的TCP客户服务器模型
- 回射客户-服务器模型(2)
- 第一章 概述 1.8客户—服务器模型
- TCPIP详解第1卷1.7分用1.8客户服务器模型1.9端口号
- 回射客户-服务器模型(3)
- 客户-服务器模型
- 客户分端--服务器 请求处理模型总结
- 守护进程之客户进程-服务器进程模型
- 领导者/追随者(Leader/Followers)模型和半同步/半异步(half-sync/half-async)模型都是常用的客户-服务器编程模型
- 回射客户-服务器模型(4)
- 用select改进回射客户-服务器模型
- IEC61850变电站基本通信结构-原理和模型_6客户-服务器
- linux环境下基于UDP的 客户端服务器模型
- Linux网络服务器epoll模型的socket通讯的实现(一)
- 基于win平台的高性能服务器底层通信模型设计(1)
- 金融风控-->客户流失预警模型-->GBDT建模
- 多线程服务器的常用编程模型——陈硕
- 第15章 高并发服务器编程(1)_非阻塞I/O模型