使用select進行SOCKET通訊

對於使用socket先要說一下socket函數,原型是:

/* Create a new socket of type TYPE in domain DOMAIN, using
   protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
   Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW;

一共有三個參數,第一個參數表示通訊時的協議簇:

AF_UNIX(本機通信)
AF_INET(TCP/IP – IPv4)
AF_INET6(TCP/IP – IPv6)

第二個參數是套接字類型

SOCK_STREAM(TCP流)
SOCK_DGRAM(UDP數據報)
SOCK_RAW(原始套接字)

第三個參數是可以用來確定前面參數的,如果前面參數確定,這裏可以填寫爲0。
socket返回的就是套接字,失敗返回-1,查看錯誤的話,就可以通過strerror(errno)獲取錯誤內容。
好了下面直接上select的服務端和客戶端,顯示服務端代碼:

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

const int SUCCESS_RESULT = 0;
const int FAILED_RESULT = -1;

// 地址
const char * ip_address = "192.168.1.57";

// 端口
const int port = 9099;

// 
const int max_line = 1024;

// 監聽隊列的上限
const int listen_count = 5;

// 客戶端上限數量
const int size_client = 10;

typedef struct server_context_st
{
	int cli_cnt;
	int cli_fds[size_client];
	fd_set all_fds;
	int max_fd;
	int srv_fd;
}server_context_st;

typedef struct tag_server_info_st {
	int cli_count;
	int cli_fd_array[size_client];
}server_info_st;

/*
 * 初始化
*/
static int init(server_context_st ** s_srv_ctx) {
	server_context_st *p = NULL;
	// 申請空間
	p = static_cast<server_context_st*>(malloc(sizeof(server_context_st)));
	if (p == NULL) {
		return FAILED_RESULT;
	}
	// 初始化
	for (int i = 0; i < size_client; i++) {
		p->cli_fds[i] = -1;
	}
	*s_srv_ctx = p;
	p = NULL;
	return SUCCESS_RESULT;
}

/*
 * 反初始化
*/
static void uninit(server_context_st ** s_srv_ctx) {
	server_context_st *p = *s_srv_ctx;
	if (p->srv_fd > 0) {
		close(p->srv_fd);
		p->srv_fd = 0;
	}
	if (p != NULL) {
		free(p);
		p = NULL;
	}
}

static int bind_server(server_context_st ** s_srv_ctx) {
	server_context_st *p = *s_srv_ctx;
	int srv_fd = 0;
	struct sockaddr_in srv_addr;
	if (p == NULL) {
		fprintf(stderr, "param error. \n");
		return FAILED_RESULT;
	}
	srv_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (srv_fd < 0) {
		fprintf(stderr, "create socket fail, errno:%s. \n", strerror(errno));
		return FAILED_RESULT;
	}
	
	int optval = 1;
	if (setsockopt(srv_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
		fprintf(stderr, "setsockopt failed! %s. \n", strerror(errno));
		return FAILED_RESULT;
	}

	bzero(&srv_addr, sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	inet_pton(AF_INET, ip_address, &srv_addr.sin_addr);
	srv_addr.sin_port = htons(static_cast<uint16_t>(port));

	if (bind(srv_fd, (struct sockaddr *)(&srv_addr), sizeof(srv_addr)) == -1) {
		fprintf(stderr, "bind error: %s. \n", strerror(errno));
		return FAILED_RESULT;
	}

	listen(srv_fd, listen_count);
	p->srv_fd = srv_fd;
	return SUCCESS_RESULT;
}

static int accept_new_client(server_context_st ** s_srv_ctx) {
	server_context_st *p = *s_srv_ctx;
	if (p == NULL) {
		fprintf(stderr, "param error. \n");
		return FAILED_RESULT;
	}

	struct sockaddr_in cli_addr;
	int cli_fd = -1;
	socklen_t address_len = 0;
	int i = 0, flags = -1;


	cli_fd = accept(p->srv_fd, (struct sockaddr*)&cli_addr, &address_len);
	if (cli_fd > 0) {
		for (i = 0; i < size_client; i++) {
			if (p->cli_fds[i] == -1) {
				flags = i;
				p->cli_fds[i] = cli_fd;
				p->cli_cnt++;
				break;
			}
		}
		if (flags >= 0) {
			fprintf(stderr, "new user client[%d] add success! \n", flags);
		}
		else {
			fprintf(stderr, "new user client add failed!%d \n", flags);
			// 說明沒有位置了
			char full_message[] = "add new client failed!";
			send(cli_fd, full_message, strlen(full_message), 0);
		}
	}
	return SUCCESS_RESULT;
}

static void recv_client_msg(server_context_st ** s_srv_ctx, fd_set* fd) {
	server_context_st *p = *s_srv_ctx;
	int i = 0, ret = 0;
	int cli_fd = 0;
	char buf[256] = { 0 };
	for (i = 0; i < p->cli_cnt; i++) {
		if (p->cli_fds[i] < 0) {
			continue;
		}
		if (FD_ISSET(p->cli_fds[i], fd)) {
			ret = static_cast<int>(read(p->cli_fds[i], buf, 256));
			if (ret > 0) {
				printf("client[%d] msg:%s.", i, buf);
			}
			else if (ret < 0) {
				fprintf(stderr, "read errno:%s", strerror(errno));
			}
			else {//客戶端退出
				printf("client[%d] exit! \n", i);
				FD_CLR(p->cli_fds[i], &p->all_fds);
				close(p->cli_fds[i]);
				p->cli_fds[i] = -1;
			}
		}
	}
}

static int monitor_client_socket(server_context_st ** s_srv_ctx) {
	server_context_st *p = *s_srv_ctx;
	int cli_fd = -1;
	int ret = 0;
	fd_set* read_fds = &(p->all_fds);
	struct timeval tv;
	int i = 0;
	char buf[256] = { 0 };

	printf(" start server .");
	while (1) {
		FD_ZERO(read_fds);

		//add standard input
		FD_SET(0, read_fds);

		FD_SET(p->srv_fd, read_fds);

		p->max_fd = p->srv_fd;

		tv.tv_sec = 30;
		tv.tv_usec = 0;

		for (i = 0; i < p->cli_cnt; i++) {
			cli_fd = p->cli_fds[i];
			if (cli_fd != -1) {
				FD_SET(cli_fd, read_fds);
			}
			p->max_fd = cli_fd > p->max_fd ? cli_fd : p->max_fd;
		}

		ret = select(p->max_fd + 1, read_fds, NULL, NULL, &tv);

		if (ret < 0) {
			fprintf(stderr, "seletc failed, errno:%s.\n", strerror(errno));
			return FAILED_RESULT;
		}
		else if (ret == 0) {
			fprintf(stderr, "seletc is timeout! error:%s.\n", strerror(errno));
		}
		else {
			// 檢測是否有輸入
			if (FD_ISSET(0, read_fds)) {
				printf("send message to \n");
				bzero(buf, 256);
				fgets(buf, 256, stdin);

				if (strlen(buf)) {
					for (i = 0; i < p->cli_cnt; i++) {
						if (p->cli_fds[i] != -1) {
							printf("client[%d] send msg:%s. \n", i, buf);
							send(p->cli_fds[i], buf, strlen(buf), 0);
						}
					}
				}
			}

			//
			if (FD_ISSET(p->srv_fd, read_fds)) {
				accept_new_client(s_srv_ctx);
			}
			
			recv_client_msg(s_srv_ctx, read_fds);
		}
		
	}
}

int main() {
	int ret = 0;
	server_context_st *srv_ctx = NULL;
	if (init(&srv_ctx) != SUCCESS_RESULT) {
		fprintf(stderr, "init failed! errno: %s.", strerror(errno));
		return -1;
	}
	ret = bind_server(&srv_ctx);
	if (ret != SUCCESS_RESULT) {
		fprintf(stderr, "socket create or bind failed. errno: %s. \n", strerror(errno));
	}
	// 等待客戶端連接,和等待客戶端的消息
	monitor_client_socket(&srv_ctx);

	uninit(&srv_ctx);
	srv_ctx = NULL;

	return 0;
}

客戶端代碼:

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

const int SUCCESS_RESULT = 0;
const int FAILED_RESULT = -1;

// 服務端地址
const char* ip_address_srv = "192.168.1.57";

// 服務端端口
const int port_srv = 9099;




int create_client_socket() {
	int local_fd = 0;
	int ret = 0;

	local_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (local_fd < 0) {
		fprintf(stderr, "create client's socket failed! errno:%s \n", strerror(errno));
		return FAILED_RESULT;
	}

	return local_fd;
}

int connect_server(int fd) {
	int ret = 0;
	struct sockaddr_in srv_addr;
	bzero(&srv_addr, sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_port = htons(port_srv);
	inet_pton(AF_INET, ip_address_srv, &srv_addr.sin_addr);

	ret = connect(fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
	if (ret < 0) {
		fprintf(stderr, "connect server failed. errno:%s. \n", strerror(errno));
		return FAILED_RESULT;
	}

	return SUCCESS_RESULT;
}

int main() {
	int cli_fd = create_client_socket();
	if (cli_fd == FAILED_RESULT) {
		return -1;
	}

	if (connect_server(cli_fd) == FAILED_RESULT) {
		return -1;
	}

	struct timeval tv;
	char buf[256] = { 0 };
	fd_set read_fds;
	int max_fd = 0, ret = 0, ret1 = 0;


	while (1) {
		FD_ZERO(&read_fds);
		//add standard input
		FD_SET(0, &read_fds);

		FD_SET(cli_fd, &read_fds);
		max_fd = cli_fd;

		tv.tv_sec = 30;
		tv.tv_usec = 0;

		ret = select(max_fd + 1, &read_fds, NULL, NULL, &tv);
		if (ret < 0) {
			close(cli_fd);			
			return -1;
		}
		else if (ret == 0) {
			// timeout 
			continue;
		}

		if (FD_ISSET(0, &read_fds)) {
			printf("send msg to server! \n");
			fgets(buf, 256, stdin);
			if (strlen(buf) > 0) {
				send(cli_fd, buf, strlen(buf), 0);
			}
			bzero(buf, 256);
		}
		if (FD_ISSET(cli_fd, &read_fds)) {
			ret1 = read(cli_fd, buf, 256);
			if (ret1 < 0) {
				continue;
			}
			else if (ret1 == 0) {
				close(cli_fd);
				return 1;
			}
			else {
				printf("recv msg :%s \n", buf);
				bzero(buf, 256);
			}
		}
	}
	return 0;


}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章