`
junfeng_feng
  • 浏览: 18246 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

简单的Client / Server 使用 linux 伯克利 socket实现

 
阅读更多

服务器:

/*
 *run command:
 *	g++ server.cpp -o server && ./server
 */

#ifndef SERVER
#define SERVER

#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<signal.h>

const int SERVPORT = 5555;//服务器监听端口号
const int BACKLOG = 10; //最大同时连接请求数
const int MAX_DATA_SIZE = 1000;

const int MAX_NAME_LENGTH = 21;
const int MAX_PHONE_LENGTH = 15;
const int MAX_HOMEADDRESS_LENGTH = 61;

struct Address
{
	char name[MAX_NAME_LENGTH];
	char phone[MAX_PHONE_LENGTH];
	char home_address[MAX_HOMEADDRESS_LENGTH];
	int age;
};
//void insert_client_fd(int* client_list,int client_fd);
//void accept_client();
//void* interact_with_client(void* clients_list);
void* send_msg_to_client(int client_fd, char* msg);
void sig_int(int signo);
void generate_phone(char* phone);
void generate_name(char* name);
void generate_rand_n_address(int n);
bool find_phone_with_name(char* name, char* phone);
bool find_name_with_phone(char* phone, char* name);
bool write_address(Address** addresses, int n);
void print_addresses();
void handle_request(const int client_id, const char* buf);
void init();

void serv();
void client_add(int* client, int fd);
void client_del(int* client, int fd);

static char const* fileName = ".address";
//sock_fd:监听socket
static int sock_fd;
/*
 * 读写锁
 */
pthread_rwlock_t rwlock;

int main(int argc, char* argv[])
{
	init();
	//accept_client();
	serv();
	return 0;
}
/*
 * 初始化
 */
void init()
{
	if (signal(SIGINT, sig_int) == SIG_ERR)
	{
		perror("signal(SIGINT,sig_int)");
	}
	if (signal(SIGQUIT, sig_int) == SIG_ERR)
	{
		perror("signal(SIGQUIT,sig_int)");
	}

	srand(time(0));
	print_addresses();
	//每次随机产生10个
	//generate_rand_n_address(10);
}

void serv()
{
	if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket创建出错");
		_exit(1);
	}

	//本地地址信息
	struct sockaddr_in my_addr;
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(SERVPORT);
	my_addr.sin_addr.s_addr = INADDR_ANY;////INADDR_ANY;inet_addr(serverIP);//
	printf("%s\n", inet_ntoa(my_addr.sin_addr));
	bzero(&(my_addr.sin_zero), 8);

	printf("before bind\n");
	if (bind(sock_fd, (struct sockaddr*) &my_addr, sizeof(my_addr)) == -1)
	{
		perror("bind出错!");
		exit(1);
	}
	printf("before listen\n");
	if (listen(sock_fd, BACKLOG) == -1)
	{
		perror("listen出错");
		exit(1);
	}

	int client[BACKLOG + 1];
	memset(client, -1, sizeof(int) * (BACKLOG + 1));

	/*
	 * 最大的fd
	 */
	int maxfd;
	fd_set rset, allset;
	FD_ZERO(&allset);

	FD_SET(sock_fd,&allset);
	maxfd = sock_fd;

	for (;;)
	{
		rset = allset;
		if (select(maxfd + 1, &rset, NULL, NULL, NULL) < 0)
		{
			perror("select error");
		}

		//client_fd:数据传输socket
		int client_fd;
		char buf[MAX_DATA_SIZE];

		//监听fd 准备好了
		if (FD_ISSET(sock_fd,&rset))
		{
			//客户端地址信息
			struct sockaddr_in remote_addr;
			socklen_t sin_size;
			sin_size = sizeof(struct sockaddr_in);
			printf("before accept\n");
			if ((client_fd = accept(sock_fd, (struct sockaddr*) &remote_addr,
					&sin_size)) == -1)
			{
				perror("accept出错\n");
				continue;
			}

			client_add(client, client_fd);
			FD_SET(client_fd,&allset);
			//调整maxfd
			if(client_fd>maxfd)
				maxfd=client_fd;

			printf("received a connection from %s assigned fd=%d\n", inet_ntoa(
					remote_addr.sin_addr), client_fd);

			if (vfork() == 0)
			{
				//服务程序:子进程
				//完成发送消息后,退出
				char buf[MAX_DATA_SIZE];
				sprintf(buf, "Hello,Your Number is %d", client_fd);
				send_msg_to_client(client_fd, buf);
				exit(0);
			}
		}
		//有客户端发来请求
		for (int i = 0; i <= BACKLOG; i++)
		{
			client_fd = client[i];
			//printf("client[%d]=%d\n",i,client_fd);
			//client_fd有请求
			if (client_fd > 0 && FD_ISSET(client_fd,&rset))
			{
				int receivebytes = recv(client_fd, buf, MAX_DATA_SIZE, 0);
				if(receivebytes<0)
				{
					perror("recv error!");
				}
				else if(receivebytes==0)
				{
					FD_CLR(client_fd,&allset);
					client_del(client,client_fd);
					close(client_fd);
				}
				else
				{
					buf[receivebytes] = '\0';
					printf("received cmd from %d:%s\n", client_fd, buf);
					if (strncmp(buf, "quit", 4) == 0)
					{
						FD_CLR(client_fd,&allset);
						client_del(client,client_fd);
						close(client_fd);
						printf("client_fd id=%lu quit\n", client_fd);
					}
					else
						handle_request(client_fd, buf);
				}
			}//if (client_fd > 0 && FD_ISSET(client_fd,&rset))
		}//for (int i = 0; i <= maxfd; i++)
	}
}
void client_add(int* client, int fd)
{
	for (int i = 0; i <= BACKLOG; i++)
	{
		if (client[i] == -1)
		{
			client[i] = fd;
			break;//居然两次,都忘记了break,编程水平太低了...
		}
	}
}
void client_del(int* client, int fd)
{
	for (int i = 0; i <= BACKLOG; i++)
	{
		if (client[i] == fd)
			client[i] = -1;
	}
}

void handle_request(const int client_id, const char* buf)
{
	char* helpString = "help #显示可用的命令\n"
		"list #显示通讯录所有内容(假设没有重名)\n"
		"name TheNameString#查询手机号\n"
		"phon ThePhoneNumberString#phone 查询名字\n"
		"shel #本地shell \n"
		"quit #quit\n"
		"inse #insert 暂时不实现\n";
	if (strncmp(buf, "help", 4) == 0)
	{
		send_msg_to_client(client_id, helpString);
	}
	else if (strncmp(buf, "list", 4) == 0)
	{

		FILE* fp = fopen(fileName, "r");
		if (fp == NULL)
		{
			perror("fopen error!");
			return;
		}
		else
		{
			Address addr;
			char buf[MAX_DATA_SIZE];
			int count = 1;
			while (fread(&addr, sizeof(Address), 1, fp) == 1)
			{
				sprintf(buf, "%-5dname:'%s',phone=%s,home_address=%s,age:%d\n",
						count, addr.name, addr.phone, addr.home_address,
						addr.age);
				//一次发送一条记录
				send_msg_to_client(client_id, buf);
				count++;
			}
		}
		fclose(fp);

	}
	else if (strncmp(buf, "name", 4) == 0)
	{
		char name[MAX_NAME_LENGTH];
		strcpy(name, buf + 5);
		printf("name=%s\n", name);
		char phone[MAX_PHONE_LENGTH];
		phone[0] = '\0';
		find_phone_with_name(name, phone);
		char buf[MAX_DATA_SIZE];
		if (strlen(phone) > 0)
		{
			sprintf(buf, "phone=%s\n", phone);
		}
		else
		{
			sprintf(buf, "No such Name=%s\n", name);
		}
		send_msg_to_client(client_id, buf);
	}
	else if (strncmp(buf, "phon", 4) == 0)
	{
		char phone[MAX_PHONE_LENGTH];
		strcpy(phone, buf + 5);
		printf("phone=%s\n", phone);
		char name[MAX_NAME_LENGTH];
		name[0] = '\0';
		find_name_with_phone(name, phone);
		char buf[MAX_DATA_SIZE];
		if (strlen(name) > 0)
		{
			sprintf(buf, "name=%s\n", name);
		}
		else
		{
			sprintf(buf, "No such Phone Number=%s\n", phone);
		}
		send_msg_to_client(client_id, buf);
	}
	/*
	 * 在线程中,不可以放在handle_request中处理
	 * (还没有找到原因)
	 */
	else
	{
		char temp[MAX_DATA_SIZE];
		sprintf(temp, "Unknow Command:%s\n\ttry help\n", buf);
		send_msg_to_client(client_id, temp);
	}
}
/*
 * 保证SIGINT,SIGQUIT,可以正常关闭连接
 */
void sig_int(int signo)
{
	printf("\nclose(%d)\nexit\n", sock_fd);
	close(sock_fd);
	_exit(0);
}

/*
 * 发送消息到client_fd
 */
void* send_msg_to_client(int client_fd, char* msg)
{
	assert(msg!=NULL);

	if (send(client_fd, msg, strlen(msg), 0) == -1)
	{
		perror("send出错\n");
	}
	return NULL;
}

/*
 * 随机产生 n 项
 */
void generate_rand_n_address(int n)
{
	Address** addresses = (Address**) malloc(sizeof(Address*) * n);

	for (int i = 0; i < n; i++)
	{
		addresses[i] = (Address*) malloc(sizeof(Address));
		generate_name(addresses[i]->name);
		generate_phone(addresses[i]->phone);
		addresses[i]->age = rand() % 100 + 10;
		printf("generate:name->%s\tphone->%s\tage=%d\n", addresses[i]->name,
				addresses[i]->phone, addresses[i]->age);
	}
	write_address(addresses, n);
	//释放空间
	for (int i = 0; i < n; i++)
		free(addresses[i]);
	free(addresses);
}
/*
 * 将数组addresses,n个address写入文件
 */
bool write_address(Address* addresses[], int n)
{

	FILE* fp = fopen(fileName, "a");
	if (fp == NULL)
	{
		perror("fopen error!\n");
		exit(1);
	}
	else
	{
		for (int i = 0; i < n; i++)
			if (fwrite(addresses[i], sizeof(Address), 1, fp) < 0)
			{
				perror("fwrite error!\n");
				exit(1);
			}
	}
	fclose(fp);
	return 1;
}

/*
 * 查找name 用电话号码 phone
 */
bool find_name_with_phone(char* name, char* phone)
{
	assert(name!=NULL);
	assert(phone!=NULL);


	FILE* fp = fopen(fileName, "r");
	if (fp == NULL)
	{
		perror("fopen error!\n");
		return 0;
	}
	else
	{
		Address addr;
		while (fread((char*) &addr, sizeof(Address), 1, fp) == 1)
		{
			if (strcmp(addr.phone, phone) == 0)
			{
				strcpy(name, addr.name);
				return 1;
			}
		}
	}

	return 0;
}
/*
 * 重名的 返回文件中第一个名字的phone
 * 成功则,phone是结果并返回1,否则返回0,phone不变
 */
bool find_phone_with_name(char* name, char* phone)
{
	assert(name!=NULL);
	assert(phone!=NULL);


	FILE* fp = fopen(fileName, "r");
	if (fp == NULL)
	{
		perror("fopen error!\n");
		return 0;
	}
	else
	{
		Address addr;
		while (fread((char*) &addr, sizeof(Address), 1, fp) == 1)
		{
			if (strcmp(addr.name, name) == 0)
			{
				strcpy(phone, addr.phone);
				return 1;
			}
		}
	}

	return 0;
}
/*
 * 将文件(若有)的所有通讯录输出到标准输出
 */
void print_addresses()
{

	FILE* fp = fopen(fileName, "r");
	if (fp == NULL)
	{
		perror("fopen error!");
		return;
	}
	else
	{
		Address addr;
		int count = 1;
		while (fread(&addr, sizeof(Address), 1, fp) == 1)
		{
			printf("%-5dname:'%s',phone=%s,home_address=%s,age:%d\n", count,
					addr.name, addr.phone, addr.home_address, addr.age);
			count++;
		}
	}
	fclose(fp);

}

/*
 * 随机生成一个字符串 长度小于 MAX_NAME_LENGTH
 */
char* alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
void generate_name(char* name)
{
	int len = rand() % MAX_NAME_LENGTH;
	if (len < 5)
	{
		len += 5;
	}
	for (int i = 0; i < len; i++)
	{
		name[i] = alphabet[rand() % (sizeof(alphabet))];
	}
	name[len] = '\0';
}

/*
 * 产生11位的电话号码
 */
char* number = "0123456789";
void generate_phone(char* phone)
{
	phone[0] = '1';
	for (int i = 1; i < 11; i++)
	{
		phone[i] = number[rand() % 10];
	}
	phone[11] = '\0';//末尾
}

#endif //

客户端:

/*
 * run command
 *		g++ client.cpp -o client && ./client 192.168.111.139#serverIP
 */

#ifndef CLIENT
#define CLIENT
#include<sys/select.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<signal.h>
#include<sys/socket.h>

const int SERVPORT = 5555;
const int MAX_DATA_SIZE = 1000;//每次最大数据传输量

//void* send_msg_to_server(void* args);
void* interact_with_server(void* agrs);
void sig_int(int signo);

static int sockfd = -1;

int main(int argc, char* argv[])
{
	if (signal(SIGINT, sig_int) == SIG_ERR)
	{
		perror("signal(SIGINT,sig_int)");
	}
	if (signal(SIGQUIT, sig_int) == SIG_ERR)
	{
		perror("signal(SIGQUIT,sig_int)");
	}

	if (argc < 2)
	{
		printf("请输入server IP\n");
		exit(1);
	}
	char* serverIP = argv[1];
	struct sockaddr_in serv_addr;
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERVPORT);
	serv_addr.sin_addr.s_addr = inet_addr(serverIP);//*((struct in_addr*)host->h_addr);
	printf("serverIP:%s\n", inet_ntoa(serv_addr.sin_addr));
	bzero(&(serv_addr.sin_zero), 8);

	int recvbytes;
	char buf[MAX_DATA_SIZE];
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket创建出错!");
		exit(1);
	}
	if (connect(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
	{
		perror("connect出错!");
		sleep(1);
	}

	if (sockfd < 0)
	{
		return NULL;
	}

	//用于select
	fd_set readSet;
	FD_ZERO(&readSet);
	FD_SET(sockfd,&readSet);
	//FD_SET(STDIN_FILENO,&readSet);

	int reseivebytes;
	//char buf[MAX_DATA_SIZE];
	while (1)
	{
		fd_set readSet_temp;
		readSet_temp = readSet;
		//一次使用 1s接收数据
		timeval tvptr;
		tvptr.tv_sec = 1;
		tvptr.tv_usec = 0;

		bool flag_recived = 0;
		int rs;
		while ((rs = select(sockfd + 1, &readSet_temp, NULL, NULL, &tvptr)) > 0)
		{
			flag_recived = 1;
			//recv准备好了
			if ((reseivebytes = recv(sockfd, buf, MAX_DATA_SIZE, 0)) == -1)
			{
				perror("recv 错误!\n");
				exit(1);
			}
			else if (reseivebytes > 0)
			{
				buf[reseivebytes] = '\0';
				printf("received msg from server:\n%s\n", buf);
			}
			else if (reseivebytes < 0)
			{
				//连接已断开
				printf("close(%d)\n", sockfd);
				close(sockfd);
				return NULL;
			}
			memset(buf, 0, MAX_DATA_SIZE);

			//readSet_temp还原
			readSet_temp = readSet;
		}
		if (rs < 0)
		{
			perror("select!");
		}
		/*
		 *没有recv不必输出提示信息
		 */
		//if(flag_recived==1)
		printf("input cmd sended to server-->");
		/*
		 * 读入一行(包含末尾的'\n'),去除'\n'='\0'
		 */
		fgets(buf, MAX_DATA_SIZE, stdin);
		/*
		 * 空行不发送
		 */
		if (strlen(buf) > 1)
		{
			buf[strlen(buf) - 1] = '\0';
			if (strncmp(buf, "shel", 4) == 0)
			{
				system(buf + 5);
			}
			else if (send(sockfd, buf, strlen(buf), 0) == -1)
			{
				perror("send 出错!\n");
			}

			if (strncmp(buf, "quit", 4) == 0)
			{
				close(sockfd);
				exit(0);
			}
		}
	}
	close(sockfd);
	return 0;
}
void sig_int(int signo)
{
	printf("\nclose(%d)\n", sockfd);
	close(sockfd);
	_exit(0);
}

#endif



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics