🏆个人主页:企鹅不叫的博客
🌈专栏
⭐️ 博主码云gitee链接:代码仓库地址
⚡若有帮助可以【关注+点赞+收藏】,大家一起进步!
💙系列文章💙
【网络编程】第一章 网络基础(协议+OSI+TCPIP+网络传输的流程+IP地址+MAC地址)
【网络编程】第二章 网络套接字(socket+UDP协议程序)
【网络编程】第三章 网络套接字(TCP协议程序+多进程+多线程+线程池)
【网络编程】第四章 网络套接字(守护进程+TCP英译汉+TCP通协议讯流程+TCP和UDP对比)
💎一、协议
🏆1.概念
协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定,比如怎么建立连接、怎么互相识别等。
🏆2.序列化和反序列化
- 为什么要序列化和反序列化?因为如果传输数据是一个字符串,我们可以一个一个字符传输,但是如果传输数据是一个结构化的数据,不能一个一个字节传输。
- 序列化将结构化的数据转变为字符串,需要编码。
- 反序列化将字符串变为结构化的数据,需要解码。
将日期结构体拼接成一个字符串
struct date
{
int year;
int month;
int day;
};
year-month-day
🏆3.网络版计算器
成员
//请求
typedef struct request{
int x; //左操作数
int y; //右操作数
char op; //操作符
}request_t;
//响应
typedef struct response{
int code; //计算状态
int result; //计算结果
}response_t;
服务器
服务器采用多线程方案,调用accept获得的套接字作为参数传递给线程
//./tcpserver _server_port
int main(int argc, char* argv[])
{
if (argc != 2){
cerr << "Usage: " << argv[0] << " port" << endl;
exit(1);
}
int port = atoi(argv[1]);
//创建套接字
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//绑定
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
cerr << "bind error!" << endl;
exit(3);
}
//监听
if (listen(listen_sock, 5) < 0){
cerr << "listen error!" << endl;
exit(4);
}
//启动服务器
struct sockaddr peer;
while(1){
socklen_t len = sizeof(peer);
int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
if (sock < 0){
cerr << "accept error!" << endl;
continue;
}
pthread_t tid;
int* p = new int(sock);
pthread_create(&tid, nullptr, Routine, p);
}
return 0;
}
如果客户端发来的计算请求存在除0、模0、非法运算等问题,就将响应结构体当中的状态字段对应设置为1、2、3即可
void* Routine(void* arg)
{
pthread_detach(pthread_self()); //分离线程
int sock = *(int*)arg;
while (1){
request_t rq;
ssize_t size = read(sock, &rq, sizeof(rq));
if (size > 0){
response_t rp = { 0, 0 };
switch (rq.op){
case '+':
rp.result = rq.x + rq.y;
break;
case '-':
rp.result = rq.x - rq.y;
break;
case '*':
rp.result = rq.x * rq.y;
break;
case '/':
if (rq.y == 0){
rp.code = 1; //除0错误
}
else{
rp.result = rq.x / rq.y;
}
break;
case '%':
if (rq.y == 0){
rp.code = 2; //模0错误
}
else{
rp.result = rq.x % rq.y;
}
break;
default:
rp.code = 3; //非法运算
break;
}
write(sock, &rp, sizeof(rp));
}
else if (size == 0){
cout << "service done" << endl;
break;
}
else{
cerr << "read error" << endl;
break;
}
}
close(sock);
return nullptr;
}
客户端
//./tcpClient _server_ip _server_port
int main(int argc, char* argv[])
{
if (argc != 3){
cerr << "Usage: " << argv[0] << " server_ip server_port" << endl;
exit(1);
}
string server_ip = argv[1];
int server_port = atoi(argv[2]);
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){
cerr << "socket error!" << endl;
exit(2);
}
//连接服务器
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(server_port);
peer.sin_addr.s_addr = inet_addr(server_ip.c_str());
if (connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0){
cerr << "connect failed!" << endl;
exit(3);
}
//发起请求
while (true){
//构建请求
request_t rq;
cout << "请输入左操作数# ";
cin >> rq.x;
cout << "请输入右操作数# ";
cin >> rq.y;
cout << "请输入需要进行的操作[+-*/%]# ";
cin >> rq.op;
write(sock, &rq, sizeof(rq));
//接收请求响应
response_t rp;
ssize_t size = read(sock, &rp, sizeof(rp));
cout << "status: " << rp.code << endl;
cout << rq.x << rq.op << rq.y << "=" << rp.result << endl;
}
return 0;
}
测试
运行服务端后再让客户端连接服务端,此时服务端就会对客户端发来的计算请求进行处理,并会将计算后的结果响应给客户端。