经过了网络编程(一)(二)的学习,完成了搭建简单的模型,但是:
- 服务器端只能等待客户端的连接,向客户端发送一条数据,然后持续该循环;
- 客户端只能在连接服务器之后接收服务器的数据,然后打印再退出。
总结:这样的简单模型能够不停地处理客户端连接,但是不能处理复杂的业务,因此本篇对该简单模型进行改造,建立能持续处理请求的C/S网络程序。
正式改造步骤:
一、服务器端的改造
拓展为八个步骤:
- 建立一个socket;
- 绑定接受客户端连接的端口bind;
- 监听网络端口listen;
- 等待接收客户端连接accept;
- 接收客户端的请求数据recv;
- 处理客户端的请求数据;
- 向客户端发送数据send;
- 关闭socket close socket。
标红的为新增步骤,其实就是多了接收客户端的请求并处理请求这个过程,增加了这个过程可以根据客户端的不同请求发送不同的数据给客户端。
改造后的代码:
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main() {
//1. 建立一个socket(传入socket族,socket类型, 协议类型)
int _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sock == -1) {
perror("scoket error!");
return 1;
}
//2. 绑定用于接受客户端连接的网络端口bind
sockaddr_in _sin = {};
_sin.sin_family = AF_INET; //协议族IPV4
_sin.sin_port = htons(4567); //端口号 host_to_net
//_sin.sin_addr.s_addr = inet_addr("127.0.0.1"); //服务器绑定的ip地址
_sin.sin_addr.s_addr= INADDR_ANY; //不限定访问该服务器的ip
if (bind(_sock, (sockaddr*)&_sin, sizeof(_sin)) == -1) {
perror("bind error!");
return 1;
} else {
printf("bind success!\n");
}
//3. 监听网络端口listen
if (listen(_sock, 5) == -1) { //套接字,最大允许连接数量
perror("listen error!");
} else {
printf("listen success!\n");
}
//4. 等待客户端连接accept
sockaddr_in clientAddr = {};
socklen_t nAddrLen = sizeof(sockaddr_in);
int _cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen); //套接字,收到客户端socket地址,返回socket地址的大小
if (_cSock == -1) {
perror("client socket error!");
} else {
printf("client socket success!");
}
//循环接收客户端数据
char _recvBuf[128] = {};
while (true) {
//5. 接收客户端的请求数据
int nLen = recv(_cSock, _recvBuf, 128, 0);
if (nLen <= 0) {
printf("client has quit!\n");
break;
}
//6. 处理请求
if (0 == strcmp(_recvBuf, "getName")) {
//7. 向客户端发送数据send
char msgBuf[] = "LIU";
send(_cSock, msgBuf, sizeof(msgBuf) + 1, 0); //长度+1,将结尾符一并发送过去
} else if (0 == strcmp(_recvBuf, "getAge")){
//7. 向客户端发送数据send
char msgBuf[] = "18";
send(_cSock, msgBuf, sizeof(msgBuf) + 1, 0); //长度+1,将结尾符一并发送过去
} else {
//7. 向客户端发送数据send
char msgBuf[] = "???";
send(_cSock, msgBuf, sizeof(msgBuf) + 1, 0); //长度+1,将结尾符一并发送过去
}
}
//8. 关闭套接字close socket
close(_sock);
printf("Server has quit!");
getchar();
return 0;
}
二、客户端的改造
拓展为七个步骤:
- 建立一个socket;
- 连接服务器;
- 用户输入请求命令scanf;
- 处理请求;
- 向服务器端发送请求send;
- 接受服务器信息recv;
- 关闭套接字close socket。
这里其实多了一个允许用户输入请求,然后客户端处理请求并发送给服务器端的过程。
改造后的代码:
//
// client.cpp
// SocketStepByStep
//
// Created by 刘君妍 on 2019/7/19.
// Copyright © 2019 tower. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define SOCKET_ERROR -1
int main() {
//1. 建立一个socket
int _sock = socket(AF_INET, SOCK_STREAM, 0); //与服务器端不同,第三个参数无需声明使用TCP连接
if (_sock == SOCKET_ERROR) {
printf("socket build error!\n");
} else {
printf("socket build success!\n");
}
//2. 连接服务器
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.s_addr = inet_addr("127.0.0.1");
int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
if (ret == SOCKET_ERROR) {
printf("connect error!\n");
} else {
printf("connect success!\n");
}
char cmdBuf[128] = {}; //大小与服务器端匹配,防止溢出
while (true) {
//3. 用户输入请求命令
scanf("%s", cmdBuf);
//4. 处理请求命令
if (0 == strcmp(cmdBuf, "exit")) {
printf("receive quit message!");
break;
} else {
//5. 向服务器端发送请求
send(_sock, cmdBuf, strlen(cmdBuf) + 1, 0);
}
//6. 接受服务器信息recv
char recvBuf[256] = {};
int nlen = recv(_sock, recvBuf, 256, 0); //返回接受数据的长度
if (nlen > 0) {
printf("message: %s\n", recvBuf);
}
}
//7. 关闭套接字close socket
close(_sock);
printf("client has quit!");
getchar();
return 0;
}