linux c socket服务端,linux c - socket select 聊天服务端

初学socket时, 只会 read, write, 那是阻塞的工作方式, 想和多个socket 工作, 要用到多线程,

今天来学习一下使用select方式进行非阻塞方式的socket交互

核心概念就是使用一个fd_set 去管理所有socket的fd.

select 方法本身是阻塞型的, 但当它管理的从多socket中有一个有读写事件时, 就会返回.

这样做的好处就是一个线程,管理多个socket的io.

几个方法

FD_ZERO(&all_set); // 初始化 fd_set

FD_SET(listen_fd, &all_set); // 把fd 添加到 fd_set

FD_ISSET(listen_fd, &r_set); // 检测新fd , 是否已经在 fd_set中存在

FD_CLR(fd, &all_set); 从fd_set中移除一个fd

话不多说, 上代码 :

wrap.h

//

// Created by gg on 26/12/2019.

//

#ifndef CHAT_WRAP_H

#define CHAT_WRAP_H

void perr_exit(const char *s);

struct sockaddr_in * ServerAddr(int port);

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);

void Bind(int fd, const struct sockaddr *sa, socklen_t salen);

void Connect(int fd, const struct sockaddr *sa, socklen_t salen);

void Listen(int fd, int backlog);

int Socket(int family, int type, int protocol);

ssize_t Read(int fd, void *ptr, size_t nbytes);

ssize_t Write(int fd, const void *ptr, size_t nbytes);

void Close(int fd);

#endif //CHAT_WRAP_H

wrap.c

//

// Created by gg on 26/12/2019.

//

#include

#include

#include

#include

#include

#include "wrap.h"

#include

#include

#include

struct sockaddr_in *ServerAddr(int port) {

struct sockaddr_in *server_addr = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));

bzero(server_addr, sizeof(struct sockaddr_in));

server_addr->sin_family = AF_INET;

server_addr->sin_addr.s_addr = htonl(INADDR_ANY);

server_addr->sin_port = htons(port);

return server_addr;

}

void perr_exit(const char *s) {

perror(s);

exit(1);

}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) {

int n;

again:

if ((n = accept(fd, sa, salenptr)) < 0) {

if ((errno == ECONNABORTED) || (errno == EINTR))

goto again;

else

perr_exit("accept error");

}

return n;

}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen) {

if (bind(fd, sa, salen) < 0)

perr_exit("bind error");

}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen) {

if (connect(fd, sa, salen) < 0)

perr_exit("connect error");

}

void Listen(int fd, int backlog) {

if (listen(fd, backlog) < 0)

perr_exit("listen error");

}

int Socket(int family, int type, int protocol) {

int n;

if ((n = socket(family, type, protocol)) < 0)

perr_exit("socket error");

return n;

}

ssize_t Read(int fd, void *ptr, size_t nbytes) {

ssize_t n;

again:

if ((n = read(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

ssize_t Write(int fd, const void *ptr, size_t nbytes) {

ssize_t n;

again:

if ((n = write(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

void Close(int fd) {

if (close(fd) == -1)

perr_exit("close error");

}

main.c

#pragma clang diagnostic push

#pragma ide diagnostic ignored "hicpp-signed-bitwise"

#include

#include

#include

#include

#include

#include

#include "wrap.h"

#define MAX_LINE 80

#define SERVER_PORT 7890

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wmissing-noreturn"

int main(int argc, char *argv[]) {

int i, maxi, maxfd, listen_fd, conn_fd, sock_fd;

int n_ready, client[FD_SETSIZE];

ssize_t n;

/** r_set : read sets, all_set : all i/o sets*/

fd_set r_set, all_set;

char buf[MAX_LINE];

char str[INET_ADDRSTRLEN];

socklen_t cli_addr_len;

struct sockaddr_in cli_addr;

struct sockaddr_in *server_addr;

/** 1. create socket */

listen_fd = Socket(AF_INET, SOCK_STREAM, 0);

server_addr = ServerAddr(SERVER_PORT);

/** 2. bind address and port */

Bind(listen_fd, (struct sockaddr *) server_addr, sizeof(struct sockaddr_in));

free(server_addr);

/** 3. listening on */

Listen(listen_fd, 20);

maxfd = listen_fd;

maxi = -1;

/** init every client fd : -1 */

for (i = 0; i < FD_SETSIZE; i++)

client[i] = -1;

/** 4.1 init all_set */

FD_ZERO(&all_set);

/** 4.2 register server fd to all_set */

FD_SET(listen_fd, &all_set);

printf("Stand by ... \n");

for (;;) {

/** 5.1 copy all_set to read set */

r_set = all_set;

/** 5.2 waiting for client connection ...

* the first param is always the max active socket fd + 1

* first param: "select" will monitor fd io event from 0 to maxfd + 1, the "maxfd + 1" equals the new connection fd

*/

n_ready = select(maxfd + 1, &r_set, NULL, NULL, NULL);

if (n_ready < 0)

perr_exit("select error");

/* new client connection, check listen_fd is readable */

if (FD_ISSET(listen_fd, &r_set)) {

cli_addr_len = sizeof(cli_addr);

conn_fd = Accept(listen_fd, (struct sockaddr *) &cli_addr, &cli_addr_len);

printf("new client_fd : %d receive from %s at PORT %d\n", conn_fd, inet_ntop(AF_INET, &cli_addr.sin_addr, str, sizeof(str)), ntohs(cli_addr.sin_port));

/* add client fd to client array */

for (i = 0; i < FD_SETSIZE; i++)

if (client[i] < 0) {

client[i] = conn_fd;

break;

}

/* max client count exceed */

if (i == FD_SETSIZE) {

fputs("too many clients\n", stderr);

exit(1);

}

/* add to all_set */

FD_SET(conn_fd, &all_set);

if (conn_fd > maxfd)

maxfd = conn_fd;

/* update client count index */

if (i > maxi)

maxi = i;

/* always true ? */

if (--n_ready == 0)

continue;

}

for (i = 0; i <= maxi; i++) { /* check all clients for data */

if ((sock_fd = client[i]) < 0)

continue;

if (FD_ISSET(sock_fd, &r_set)) {

/** close lost socket client */

if ((n = Read(sock_fd, buf, MAX_LINE)) == 0) {

Close(sock_fd);

FD_CLR(sock_fd, &all_set);

client[i] = -1;

}

else {

for (int k = 0; k <= maxi; ++k) {

int client_fd = client[k];

if (client_fd > -1 && client_fd != sock_fd)

Write(client_fd, buf, n);

}

}

if (--n_ready == 0)

break;

}

}

}

return 0;

}

#pragma clang diagnostic pop

#pragma clang diagnostic pop

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值