前言
从作为一名数据分析从业者,一直不太了解后台开发岗位所使用的各种网络编程方法,于是也就老想闹明白,这个东西究竟是咋玩的? 本文试图通过一个小案例,来管窥下这个领域的编程套路。参考的书籍主要是《TCP/IP网络编程》(尹圣丽 著 金国哲 译)。
我们知道,网络编程主要就是实现服务端与客户端的通信,那么就涉及到一系列通信过程的协议,也就是双方要商量好有一套共同的语境。这就又跳到计算机网络知识中的OSI网络体系模型,共分了7层(具体可参考相关书籍)。实际应用中,可简化理解为4层,从上往下为 应用层、传输层、网络层、链路层。TCP就是传输层中的一种协议,是一种基于连接的,比较可靠的,不会发生数据丢失的协议,相对应的是UDP协议。 IP对应的是网络层,这其中也有一堆不同的协议,如IPv4, IPv6等。
在编写网络通信过程时,最重要的几个环节如下:
对于服务端: 建立套接字、绑定网络地址、监听、接受、读取/写入、关闭连接
对于客户端:建立套接字、请求连接、读取/写入、关闭连接
案例问题
1. 写一个server,根据客户端传入的两个数值,进行数学计算,如加减乘除。
2. 写一个client,向server传入若两个数值,以及所需的计算方法
练习过程
以下练习过程在一台笔记本上完成,server, client部署在同一个机器上。编程语言为C。
服务端代码如下:
#include
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 1024
long doMathWork(char op, int num1, int num2);
int main(int argc, char* argv[]){
int server_sock, client_sock;
char msg[BUF_SIZE];
size_t str_len;
//声明网络地址的结构体
struct sockaddr_in server_addr; //服务端网络地址
struct sockaddr_in client_addr; //客户端网络地址
int i;
socklen_t client_addr_size;
//声明计算类型, 以及两个数值变量
char op;
int num1, num2;
long result;
if(argc != 2){
printf("Usage: %s \n", argv[0]);
exit(1);
}
char* port = argv[1];
//第1步, 建立服务端套接字
server_sock = socket(PF_INET /*IPv4*/,SOCK_STREAM /*TCP*/,0);
if(server_sock == -1){
fputs("socket error", stderr);
fputc('\n', stderr);
exit(-1);
}
//第2步, 绑定网络地址
//定义网络地址结构体
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET; //IPv4地址族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(atoi(port));
if(bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
fputs("bind error", stderr);
fputc('\n', stderr);
exit(-2);
}
//第3步, 建立监听
if(listen(server_sock, 1) == -1){
fputs("listen error", stderr);
fputc('\n', stderr);
exit(-3);
}
//第4步, 接受访问
client_addr_size = sizeof(client_addr);
client_sock = accept(server_sock, (struct sockaddr*)&client_sock, &client_addr_size);
if(client_sock == -1){
fputs("accept error", stderr);
fputc('\n', stderr);
exit(-4);
}else{
printf("Connected to client........\n");
}
//第5步, 传输内容
//首先接收一个计算类型, 如加减乘除
read(client_sock, msg, BUF_SIZE-1);
msg[strlen(msg)-1] = 0;
op = msg[0];
printf("op: %c \n", op);
read(client_sock, msg, BUF_SIZE-1);
num1 = atoi(msg);
printf("num1: %d \n", num1);
read(client_sock, msg, BUF_SIZE-1);
num2 = atoi(msg);
printf("num2: %d \n", num2);
result = doMathWork(op, num1, num2);
write(client_sock, (char*)&result, sizeof(result));
//第6步, 关闭连接
close(client_sock);
close(server_sock);
return 0;
}
long doMathWork(char op, int num1, int num2){
long result = 0L;
switch(op){
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if(num2 == 0){
fputs("cannot divide zero",stderr);
fputc('\n',stderr);
exit(-1);
}
result = num1 / num2;
;
default:
printf("You have input %c \n", op);
fputs("only allow input: + - * / \n", stderr);
break;
}
printf("result: %li \n" , result);
return result;
}
#include
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 1024
//注意到服务端程序中,有多个地方使用类似的逻辑来处理函数返回值,故可定义一个公用的函数
void error_msg(char* message);
int main(int argc, char* argv[]){
int client_sock;
struct sockaddr_in server_addr;
char* message[BUF_SIZE];
int str_len;
long result;
char* ip;
char* port;
if(argc != 3){
printf("Usage: %s " , argv[0]);
exit(-1);
}
ip = argv[1];
port = argv[2];
//第1步, 建立套接字
client_sock = socket(PF_INET,SOCK_STREAM,0);
if(client_sock == -1) error_msg("socket() error");
//第2步, 连接服务端套接字
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(atoi(port));
if(connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
error_msg("connect() error");
}else{
puts("Connection Created ............\n");
}
//第3步, 通信内容
printf("Input a kind of opeator(+ - * /): ");
fgets((char *) message, BUF_SIZE, stdin);
write(client_sock, message, strlen((const char *) message));
printf("Input the first number: ");
fgets((char *) message,BUF_SIZE, stdin);
write(client_sock, message, strlen((const char *) message));
printf("Input the second number: ");
fgets((char *) message,BUF_SIZE, stdin);
write(client_sock, message, strlen((const char *) message));
read(client_sock, &result, 4);
printf("math result: %li \n", result);
//第4步, 关闭连接
close(client_sock);
return 0;
}
void error_msg(char* message){
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
服务端:
客户端: