utili.h:
#ifndef _UTILI_H
#define _UTILI_H
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#define BUFFER_MAX_SIZE 256
#define LISTEN_QUEUE_SIZE 5
//#define LISTEN_PORT 8080
//#define LISTEN_IP "127.0.0.1"
#define COMMAND_MAX_SIZE 10
typedef enum {ADD, SUB, MUL, DIV, MOD, QUIT} CMD_TYPE;
typedef struct oper_st
{
int op1;
int op2;
CMD_TYPE oper;
}oper_st;
int startup(const char *ip, short port)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket.");
exit(1);
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
//address.sin_addr.s_addr = INADDR_ANY;
address.sin_addr.s_addr = inet_addr(ip);
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
socklen_t addrlen = sizeof(struct sockaddr);
int ret = bind(sockfd, (struct sockaddr*)&address, addrlen);
if(ret == -1)
{
perror("bind.");
close(sockfd);
exit(1);
}
ret = listen(sockfd, LISTEN_QUEUE_SIZE);
if(ret == -1)
{
perror("listen.");
close(sockfd);
exit(1);
}
return sockfd;
}
#endif // _UTILI_H
ser.c:
#include"../utili.h"
#include<sys/select.h>
#define MAX_CLIENT_NUM 5
void data_handler(int sockConn);
int main(int argc, char *argv[])
{
int sockSer = startup(argv[1], atoi(argv[2]));
fd_set set;
int client_fd[MAX_CLIENT_NUM] = {0};
int client_connect_num = 0;
int max_sock = sockSer;
int i;
while(1)
{
FD_ZERO(&set);
FD_SET(sockSer, &set);
for(i=0; i<MAX_CLIENT_NUM; ++i)
{
if(client_fd[i] != 0)
{
FD_SET(client_fd[i], &set);
}
}
int ret = select(max_sock+1, &set, NULL, NULL, NULL);
if(ret == -1)
{
//error
perror("select.");
continue;
}
else if(ret == 0)
{
//tiemout
printf("Server Time Out.\n");
continue;
}
else
{
//1 accept
if(FD_ISSET(sockSer, &set))
{
struct sockaddr_in addrCli;
socklen_t addrlen = sizeof(struct sockaddr);
int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);
if(sockConn == -1)
{
perror("accept.");
continue;
}
if(client_connect_num >= MAX_CLIENT_NUM)
{
char *msg = "Server Over Load.";
send(sockConn, msg, strlen(msg)+1, 0);
close(sockConn);
continue;
}
client_fd[client_connect_num++] = sockConn;
if(sockConn > max_sock)
max_sock = sockConn;
}
//2 recv send
else
{
for(i = 0; i<MAX_CLIENT_NUM; ++i)
{
if(FD_ISSET(client_fd[i], &set))
{
data_handler(client_fd[i]);
}
}
}
}
}
close(sockSer);
return 0;
}
void data_handler(int sockConn)
{
char msg[256];
recv(sockConn, msg, 256, 0);
printf("from client msg:> %s\n",msg);
send(sockConn, msg, strlen(msg)+1, 0);
}
cli.c:
#include"utili.h"
int main(int argc, char *argv[])
{
int sockCli = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(atoi(argv[2]));
addrSer.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t addrlen = sizeof(struct sockaddr);
connect(sockCli, (struct sockaddr*)&addrSer, addrlen);
char msg[256];
char recvmsg[256];
while(1)
{
printf("input msg:>");
scanf("%s",msg);
send(sockCli, msg, strlen(msg)+1, 0);
recv(sockCli, recvmsg, 256, 0);
printf("from server self msg:> %s\n",recvmsg);
}
close(sockCli);
return 0;
}
该程序实现了IO复用下的,回显服务器模型(客户端发送消息给服务器,服务器收到信息后,再发送给客户端),用select模式服务器可以同时为多个客户端提供服务。本程序后续仍然需要改进,当一个客户端退出的时候,向服务器发送一个信号,服务器就退出服务了,那么这个服务器的健壮性就太差了。
以下的程序,实现了客户端选择想进行的操作和两个操作数,并以结构体的形式发送给服务器,服务器计算完成之后,再把计算结果返回到客户端。
utili.h:
#ifndef _UTILI_H
#define _UTILI_H
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#define BUFFER_MAX_SIZE 256
#define LISTEN_QUEUE_SIZE 5
//#define LISTEN_PORT 8080
//#define LISTEN_IP "127.0.0.1"
#define COMMAND_MAX_SIZE 10
typedef enum {ADD, SUB, MUL, DIV, MOD, QUIT} CMD_TYPE;
typedef struct oper_st
{
int op1;
int op2;
CMD_TYPE oper;
}oper_st;
int startup(const char *ip, short port)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket.");
exit(1);
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
//address.sin_addr.s_addr = INADDR_ANY;
address.sin_addr.s_addr = inet_addr(ip);
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
socklen_t addrlen = sizeof(struct sockaddr);
int ret = bind(sockfd, (struct sockaddr*)&address, addrlen);
if(ret == -1)
{
perror("bind.");
close(sockfd);
exit(1);
}
ret = listen(sockfd, LISTEN_QUEUE_SIZE);
if(ret == -1)
{
perror("listen.");
close(sockfd);
exit(1);
}
return sockfd;
}
#endif // _UTILI_H
ser.c:
#include"utili.h"
#include<sys/select.h>
#define MAX_CLIENT_NUM 5
void data_handler(int sockConn);
int main(int argc, char *argv[])
{
int sockSer = startup(argv[1], atoi(argv[2]));
fd_set set;
int client_fd[MAX_CLIENT_NUM] = {0};
int client_connect_num = 0;
int max_sock = sockSer;
int i;
while(1)
{
FD_ZERO(&set);
FD_SET(sockSer, &set);
for(i=0; i<MAX_CLIENT_NUM; ++i)
{
if(client_fd[i] != 0)
{
FD_SET(client_fd[i], &set);
}
}
int ret = select(max_sock+1, &set, NULL, NULL, NULL);
if(ret == -1)
{
//error
perror("select.");
continue;
}
else if(ret == 0)
{
//tiemout
printf("Server Time Out.\n");
continue;
}
else
{
//1 accept
if(FD_ISSET(sockSer, &set))
{
struct sockaddr_in addrCli;
socklen_t addrlen = sizeof(struct sockaddr);
int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);
if(sockConn == -1)
{
perror("accept.");
continue;
}
if(client_connect_num >= MAX_CLIENT_NUM)
{
char *msg = "Server Over Load.";
send(sockConn, msg, strlen(msg)+1, 0);
close(sockConn);
continue;
}
client_fd[client_connect_num++] = sockConn;
if(sockConn > max_sock)
max_sock = sockConn;
}
//2 recv send
else
{
for(i = 0; i<MAX_CLIENT_NUM; ++i)
{
if(FD_ISSET(client_fd[i], &set))
{
data_handler(client_fd[i]);
}
}
}
}
}
close(sockSer);
return 0;
}
void data_handler(int sockConn)
{
oper_st op;
int result;
recv(sockConn, &op, sizeof(op), 0);
if(op.oper == ADD)
result = op.op1 + op.op2;
else if(op.oper == SUB)
result = op.op1 - op.op2;
else if(op.oper == MUL)
result = op.op1 * op.op2;
else if(op.oper == QUIT)
{
printf("game over.");
}
send(sockConn, &result, sizeof(int), 0);
}
cli.c:
#include"utili.h"
int main(int argc, char *argv[])
{
int sockCli = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(atoi(argv[2]));
addrSer.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t addrlen = sizeof(struct sockaddr);
connect(sockCli, (struct sockaddr*)&addrSer, addrlen);
char cmd[COMMAND_MAX_SIZE];
oper_st op;
int result;
while(1)
{
printf("input cmd:>"); //add sub mul div mod quit
scanf("%s",cmd);
if(strcmp(cmd, "add") == 0)
op.oper = ADD;
else if(strcmp(cmd, "sub") == 0)
op.oper = SUB;
else if(strcmp(cmd, "mul") == 0)
op.oper = MUL;
else if(strcmp(cmd, "div") == 0)
op.oper = DIV;
else if(strcmp(cmd, "mod") == 0)
op.oper = MOD;
else if(strcmp(cmd, "quit") == 0)
{
op.oper = QUIT;
send(sockCli, &op, sizeof(op), 0);
break;
}
printf("input op1 and op2:>");
scanf("%d %d", &op.op1, &op.op2);
send(sockCli, &op, sizeof(op), 0);
recv(sockCli, &result, sizeof(int), 0);
printf("result = %d\n",result);
}
close(sockCli);
return 0;
}
本程序和上面的程序存在一样的问题,但程序设计的基本功能可以实现,可以为不同的客户端提供计算服务。问题的关键在于select模式如何对客户端的离开做出分支处理,保证服务器不退出,进而继续为其他客户端提供服务。
网络编程真的博大精深,Unix卷一、卷二、linux高性能服务器都是需要好好啃的书。