對於使用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;
}