I/O多路复用通信连接select篇
///////////////////////////////////////////////////////////////////////////////////////////////////////////// 以下代码功能实现的是一简单的客户端与服务端的聊天程序,在UNIX下全 // C写的,功能简单但使用的知识点瞒多,也算是经典入门级编程了,参考了 // steven大师的unix网络编程与unix环境高级编程书。供个人与大家参考。 // 希望高手指点。附件是整个程序和makefile文件。需在linux下编译。备有 //gcc. /////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////// /// buffer头文件, the name is buffer.h ////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef _BUFFER_H_ #define _BUFFER_H_![]() typedef struct buffer { char *p; unsigned long size; unsigned long length; unsigned long maxsize; } buffer_t;![]() buffer_t * buffer_create (unsigned long size, unsigned long maxsize); void buffer_destory (buffer_t * buf); int buffer_append_data (buffer_t * buf, char *data, unsigned long length); int buffer_resize (buffer_t * buf, int newsize); char * buffer_fetch_data(buffer_t * buf, unsigned long length, unsigned long * rlength); int buffer_remove_data(buffer_t * buf, unsigned long length); unsigned long buffer_get_length(buffer_t * buf); unsigned long buffer_get_size(buffer_t * buf); unsigned long buffer_get_maxsize(buffer_t *buf); #endif以上代码为buffer头文件代码, 下面的代码为buffer建立的代码,用于存储聊天信息。 //buffer.c文件 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h>![]() #include "buffer.h" ![]() buffer_t * buffer_create (unsigned long size, unsigned long maxsize) { buffer_t *buf; if (size == 0 && maxsize == 0) { return NULL; } if (maxsize < size) { maxsize = size; } if ((buf = (buffer_t *) malloc (sizeof (buffer_t))) == NULL) { fprintf (stderr, "allocate memory failed:%s\n", strerror (errno)); return NULL; }![]() buf->size = size; buf->maxsize = maxsize; buf->length = 0;![]() if ((buf->p = (char *) malloc (sizeof (buf->size))) == NULL) { fprintf (stderr, "allocate memory failed:%s\n", strerror (errno)); free (buf); return NULL; }![]() return buf; ![]() } ![]() void buffer_destory (buffer_t * buf) { if (buf) { if (buf->p) { free (buf->p); buf->p = NULL; } } free (buf); buf = NULL; }![]() int buffer_append_data (buffer_t * buf, char *data, unsigned long length) { if (!buf || !buf->p) { return -1; } if ((buf->size - buf->length) > length) { memcpy (buf->p + buf->length, data, length); buf->length += length; return 0; } else { int n;![]() if ((n = buffer_resize (buf, buf->length + length)) >= buf->length + length) { memcpy (buf->p + buf->length, data, length); buf->length += length; return 0; } else { //TODO:![]() if (n == -1) { fprintf (stderr, "Object %p is bad.\n", buf); } else if (n == -2) { fprintf (stderr, "Expected newsize of object %p is short than original size.\n", buf); } else { fprintf (stderr, "Object %p is left untouched.\n", buf); }![]() return -1; } }![]() return 0; }![]() int buffer_resize (buffer_t * buf, int newsize) { unsigned long size; char *p; if (!buf || !buf->p) { return -1; } if (buf->size > newsize) { return -2; } size = newsize; if (buf->maxsize < size) { size = buf->maxsize; } if ((p = realloc (buf->p, size)) == NULL) { return buf->size; } else { buf->p = p; buf->size = size; return size; } }![]() char * buffer_fetch_data (buffer_t * buf, unsigned long length, unsigned long *rlength) { char *p; if ((p = malloc (length)) < 0) { return NULL; } if (buf->length > length) { *rlength = length; } else { *rlength = buf->length; } memcpy (p, buf->p, *rlength); if (buf->length - *rlength > 0) { memmove (buf->p, buf->p + *rlength, buf->length - *rlength); buf->length = buf->length - *rlength; } else { buf->length = 0; }![]() return p; }![]() int buffer_remove_data (buffer_t * buf, unsigned long length) { if (length < buf->length) { memmove (buf->p, buf->p + length, buf->length - length); buf->length = buf->length - length; } else { buf->length = 0; } return 0; }![]() unsigned long buffer_get_length (buffer_t * buf) { return buf->length; }![]() unsigned long buffer_get_size (buffer_t * buf) { return buf->size; }![]() unsigned long buffer_get_maxsize (buffer_t * buf) { return buf->maxsize; }下面是聊天程序的主文件。 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// unix下的网络编程程序,用C写的,前面用中文说明了下,人懒后不愿意写后面的了使用 // 的是UNIX网络编程函数,实现了多用户连接服务器并发送消息。没有上传客户端程序。但 // 原理大同小异, //,添加使用buffer来保存信息,此buffer主要建立程序在buffer.c中,编译写了个简单的 // makefile. // 程序还有很大的bug,学习之作,勿笑,期望以后成为牛人。 // writed by carywu //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // tcp_connection_server.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <arpa/inet.h>![]() #include "buffer.h" ![]() #define LISTENQ 5 #define BUFFER_SIZE 1024 #define BUFFER_MAXSIZE 4096![]() //////////////////////////////////////////////////////////////////////////////// // 定义缓存结构体//////////////////////////////////////////////////////////////////////////////// ![]() typedef struct connection { int fd;![]() buffer_t *rbuf; buffer_t *wbuf;![]() } connection_t; ![]() //////////////////////////////////////////////////////////////////////////////// // 全局定义//////////////////////////////////////////////////////////////////////////////// ![]() fd_set global_read_set; fd_set global_write_set; fd_set global_except_set;![]() int global_max_fd = 0; ![]() connection_t **global_connection_list = NULL; int global_connection_count = 0; int global_support_connection_num;![]() int network_init (int n); connection_t * connection_create (int fd); void connection_destroy (connection_t * c); void network_finial (void); void network_register_read (int fd); void network_unregister_read (int fd); void network_register_write (int fd); void network_unregister_write (int fd); void network_register_except (int fd); void network_unregister_except (int fd); void data_exchange(int src);![]() //////////////////////////////////////////////////////////////////////////////// // 主程序//////////////////////////////////////////////////////////////////////////////// int main (int argc, char **argv) { if (argc < 3) //判断程序所带参数个数,小于两个出错处理, { fprintf (stderr, "Usage:%s <IP><PORT>\n", argv[1]); //FIXME: 退出不是好的做法,在这为了简便而使用。 exit (1); } //XXX: step 1 create a socket int server_listen_socket;![]() if ((server_listen_socket = socket (PF_INET, SOCK_STREAM, 0)) < 0) //新建socket,文件描述服为server_listen_socket { fprintf (stderr, "socket() failed:%s\n", strerror (errno)); exit (1); } //XXX: step 2 create a bind int on; on = 1;![]() if (setsockopt (server_listen_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) //设置socket,如果&on 非0,则重用bind地址。 { fprintf (stderr, "setsockopt failed:%s\n", strerror (errno)); exit (1); } #ifdef _SO_REUSEPORT_ //中间代码在定义了_SO_REUSEPORT_情况下生效,为重用bind的端口。 on = 1; if (setsockopt (server_listen_socket, SOL_SOCKET, SO_REUSEPORT, &on, sizeof (on)) < 0) { fprintf (stderr, "setsockopt failed:%s\n", strerror (errno)); exit (1); } #endif struct sockaddr_in server_address; //将socket 与地址和端口绑定,地址与端口来之argv memset (&server_address, 0, sizeof (server_address));![]() server_address.sin_family = PF_INET; server_address.sin_port = htons (atoi (argv[2])); server_address.sin_addr.s_addr = inet_addr (argv[1]);![]() if (bind (server_listen_socket, (struct sockaddr *) &server_address, sizeof (server_address)) < 0) { fprintf (stderr, "bind() failed:%s\n", strerror (errno)); //出错处理,退出非明智选择。 exit (1); } //XXX: step 3: create a listen if (listen (server_listen_socket, LISTENQ) < 0) //监听此socket { fprintf (stderr, "listen() failed:%s\n", strerror (errno)); exit (1); }![]() //XXX: below is the function of select network_init (1024); // 初始化select,程序被写成了函数,见下。![]() network_register_read (server_listen_socket);// 如上。 ![]() struct timeval to;//时间结构体,用于超时用。 ![]() int n; ![]() fd_set rset; fd_set wset; fd_set eset;![]() //XXX: the main loop ![]() for (;;) { rset = global_read_set; wset = global_write_set; eset = global_except_set;![]() to.tv_sec = 1; to.tv_usec = 0;![]() if ((n = select (global_max_fd + 1, &rset, &wset, &eset, &to)) < 0)// select 语句。 { if (errno == EINTR) { fprintf (stdout, "closed by signal,continue.....\n"); continue; } else if (errno == EAGAIN) { continue; } else { fprintf (stderr, "select() failed:%s\n", strerror (errno)); exit (1); } } else if (n == 0) { fprintf (stdout, "timeout......\n"); } else { fprintf (stdout, "%d fd is waiting for reading or writing or excepting\n", n);![]() int i; ![]() for (i = 0; i <= global_max_fd; i++) { if (FD_ISSET (i, &rset)) { if (i == server_listen_socket) { fprintf (stdout, "fd %d is ready for accepting\n", i); //XXX: below is the accept![]() int server_accept_socket; ![]() struct sockaddr_in peer_address; socklen_t peer_address_length;![]() peer_address_length = sizeof (peer_address); ![]() if ((server_accept_socket = accept (server_listen_socket, (struct sockaddr *) &peer_address, &peer_address_length)) < 0) { fprintf (stderr, "accept() failed:%s\n", strerror (errno)); exit (1); } else { fprintf (stdout, "New tcp connection %d from (%s:%d)\n", server_accept_socket, inet_ntoa (peer_address.sin_addr), ntohs (peer_address.sin_port));![]() network_register_read (server_accept_socket); ![]() connection_t *conn; ![]() //TODO: the connect_create conn = connection_create(server_accept_socket); global_connection_list[server_accept_socket] = conn;![]() } ![]() } else { //can do read ssize_t readn; char buffer[BUFFER_SIZE]; fprintf(stdout, "fd %d is ready for reading\n", i); again: if ((readn = read (i, buffer, BUFFER_SIZE)) < 0) { if (errno == EINTR) { goto again; } else { fprintf (stderr, "read error on socket %d\n", i); } } else if (readn == 0) { fprintf (stdout, "connection was closed by peer\n"); close (i); network_unregister_read (i); network_unregister_write (i); network_unregister_except (i);![]() connection_destroy(global_connection_list[i]); global_connection_list[i] = NULL; } else { buffer[readn] = '\0'; fprintf (stdout, "read %d bytes on socket %d,%s\n", readn, i, buffer); buffer_append_data(global_connection_list[i]->rbuf, buffer, readn);![]() data_exchange(i); } } } //TODO: check the writable if (FD_ISSET (i, &wset)) { fprintf (stdout, "fd %d is ready for writing\n", i);![]() ssize_t written; retry: if ((written = write (i, global_connection_list[i]->wbuf->p, global_connection_list[i]->wbuf->length)) < 0) { if (errno == EINTR) |



///////////////////////////////////////////////////////////////////////////////////////////////////////////