`
junfeng_feng
  • 浏览: 19925 次
  • 性别: 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



分享到:
评论

相关推荐

    跨平台的socket库,windows-linux-socket

    本文将深入探讨“跨平台的socket库,windows-linux-socket”这一主题,旨在帮助开发者理解如何在Windows和Linux操作系统之间实现兼容的Socket编程。 首先,Socket是操作系统提供的一种接口,用于在网络中进行进程间...

    socket 实现多人聊天

    为了实现多人聊天,我们需要创建一个服务器端(Server)和多个客户端(Client)。服务器端需要监听来自多个客户端的连接请求,这就涉及到`listen()`函数,它使Socket进入监听状态。当有新的连接请求时,服务器调用`...

    Linux环境下实时系统的Socket通讯.pdf

    本文主要探讨如何在Linux环境中利用Berkeley Socket实现TCP/IP协议的实时通信。 首先,Socket是由伯克利大学的研究团队在80年代开发的,它被移植到UNIX操作系统中,形成了所谓的Berkeley Socket,为不同进程或站点...

    socket-windows-and-linux.zip_Linux windows socket_linux c++ sock

    标题"socket-windows-and-linux.zip_Linux windows socket_linux c++ sock"表明了本主题涉及的是跨平台的Socket编程,主要关注Linux和Windows操作系统下,使用C++语言实现的Socket接口。描述提到"利用socket函数实现...

    Linux 下使用C++进行Socket编程(源码)

    在Linux中,Socket API是通过伯克利套接字(Berkeley Sockets)实现的,这是一个广泛使用的接口,支持多种网络协议,如TCP(传输控制协议)和UDP(用户数据报协议)。 1. **TCP Socket编程**:TCP是一种面向连接的...

    C实现基于Socket实现自定义协议通信

    在C语言中,我们通常使用伯克利套接字(BSD Socket)API来实现Socket编程。该API源于BSD操作系统,现在广泛应用于各种操作系统,包括Linux和Windows。 创建Socket首先需要调用`socket()`函数,它返回一个描述符,...

    linux socket程序设计源代码

    在Linux操作系统中,Socket是一种进程间通信机制,它允许不同进程或者网络间的进程进行通信。在本主题"Linux Socket程序设计源代码"中,我们将会深入探讨如何在Linux环境下编写客户端和服务器端的Socket程序。 首先...

    伯克利socket编程

    伯克利socket编程 Socket编程基础本章以Berkeley Socket为主,主要介绍网络编程时常用的调用和程序使用它们的方法及基本结构。

    Linux下Socket编程

    在Linux操作系统中,Socket编程是实现网络通信的核心技术。它为进程间通信(IPC)提供了通用接口,使得不同网络协议的应用程序能够交换数据。本文将深入探讨Linux下的Socket编程,结合提供的资源,如"Linux框架图....

    l-sock.zip_linux socket_socket_socket linux_zip

    1. **套接字API**:在Linux中,Socket API是基于伯克利套接字(Berkeley Sockets)模型的,提供了一系列函数,如`socket()`, `bind()`, `listen()`, `accept()`, `connect()`, `send()`, `recv()`等,用于创建、配置...

    VS2008 C Socket 通信程序 Client1(代替原来的“VS2008 C Socket 通信程序 Client”,原来的文件已损坏)

    在C语言中,我们通常使用伯克利套接字API(Berkeley Sockets API)来实现Socket编程。 1. **创建Socket**: 在VS2008中,我们需要包含`winsock2.h`头文件,并初始化Winsock库。通过调用`WSAStartup()`函数,我们...

    linux下用socket+Qt实现的TraceRoute程序

    在Linux下,socket接口遵循伯克利套接字(Berkeley Sockets)API。TraceRoute的实现需要创建一个UDP socket,因为Traceroute利用了UDP的特性——无连接和不保证数据传输。 2. **ICMP协议与TTL超时**:Traceroute...

    win-socket-demo_socket编程_socket_windows_DEMO_

    “socket windows DEMO”进一步说明这是一个关于Windows环境下的Socket编程示例,可能包含了一个简单的服务器(socket_server.cpp)和客户端(socket_client.cpp)程序。服务器端通常会监听特定的端口,等待客户端的...

    Linux下实现类似QQ的通信

    在Linux下,Sockets是通过伯克利套接字API(Berkeley Sockets API)来实现的,这是一个标准的接口,广泛用于各种网络编程。 1. **服务器端(SERVER端)**: - **创建Socket**:首先,服务器端需要创建一个Socket,...

    ftp.rar_C++ SOCKET FTP_FTP SERVER_FTP server client_c++ftp_socke

    在C++中实现SOCKET编程,通常需要包含`&lt;winsock2.h&gt;`或`&lt;sys/socket.h&gt;`头文件,使用Winsock库(Windows)或伯克利套接字API(Unix-like系统)。基本步骤包括初始化(`WSAStartup`),创建套接字(`socket`),绑定...

    应用于Linux下的Socket通信机制.pdf

    Linux操作系统中的Socket通信机制是网络编程中的核心组件,它提供了进程间通过网络进行通信的接口。Socket已经成为多种操作系统网络API的基础,特别是在Linux环境下,无论是基于TCP还是UDP的Socket通信技术都备受...

    Socket实现qq聊天

    在C++中,通常使用伯克利套接字(Berkeley Sockets)接口来实现Socket编程。 1. **TCP/IP协议**:TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,而IP(Internet Protocol)是无连接的...

    Linux环境下基于TCP的Socket编程浅析.pdf

    Socket接口最初是在伯克利软件发布版(BSD)Unix中实现的,因此通常被称为“BSD Socket”。一个Socket描述了一个通信连接的一端,每个参与通信的程序都需要一个Socket来描述其通信端点。不同的主机可以通过各自的...

    socket库函数

    可以通过调用`fcntl()`函数或使用`setsockopt()`函数设置SOCKET的非阻塞选项来实现这一功能。 #### 八、Cisco IOS for S/390与BSD UNIX的区别 尽管Cisco IOS for S/390支持大部分BSD Sockets的功能,但在某些细节...

Global site tag (gtag.js) - Google Analytics