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建立的代码,用于存储聊天信息。
#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)
{
goto retry;
}
else
{
fprintf (stderr, "write failed on socket:%d:%s\n",
i, strerror (errno));
}
}
else
{
connection_t *current;
current = global_connection_list[i];
buffer_remove_data (current->wbuf, written);
if (current->wbuf->length == 0)
{
network_unregister_write(i);
}
}
}
}
}
}
close (server_listen_socket);
network_finial();
return 0;
}
int
network_init (int n)
{
global_support_connection_num = n;
if ((global_connection_list =
malloc (global_support_connection_num * sizeof (connection_t *))) ==
NULL)
{
fprintf (stderr, "malloc() failed:%s\n", strerror (errno));
exit (1);
}
int i;
for (i = 0; i < global_support_connection_num; i++)
{
global_connection_list[i] = NULL;
}
global_connection_count = 0;
FD_ZERO (&global_read_set);
FD_ZERO (&global_write_set);
FD_ZERO (&global_except_set);
return 0;
}
connection_t * connection_create (int fd)
{
connection_t *conn;
if ((conn = (connection_t *) malloc (sizeof (connection_t))) == NULL)
{
fprintf (stderr, "malloc failed:%s\n", strerror (errno));
return NULL;
}
conn->fd = fd;
conn->rbuf = buffer_create (BUFFER_SIZE, BUFFER_MAXSIZE);
conn->wbuf = buffer_create (BUFFER_SIZE, BUFFER_MAXSIZE);
return conn;
}
void connection_destroy (connection_t * c)
{
if (c)
{
if (c->rbuf)
{
buffer_destory (c->rbuf);
c->rbuf = NULL;
}
if (c->wbuf)
{
buffer_destory (c->wbuf);
c->wbuf = NULL;
}
free (c);
c = NULL;
}
}
void network_finial (void)
{
int i;
for (i = 0; i < global_support_connection_num; i++)
{
if (global_connection_list[i] != NULL)
{
connection_destroy (global_connection_list[i]);
}
}
}
void network_register_read (int fd)
{
FD_SET (fd, &global_read_set);
if (fd > global_max_fd)
{
global_max_fd = fd;
}
}
void network_unregister_read (int fd)
{
FD_CLR (fd, &global_read_set);
}
void network_register_write (int fd)
{
FD_SET (fd, &global_write_set);
if (fd > global_max_fd)
{
global_max_fd = fd;
}
}
void network_unregister_write (int fd)
{
FD_CLR (fd, &global_write_set);
}
void network_register_except (int fd)
{
FD_SET (fd, &global_except_set);
if (fd > global_max_fd)
{
global_max_fd = fd;
}
}
void network_unregister_except (int fd)
{
FD_CLR (fd, &global_except_set);
}
void data_exchange(int src)
{
int i;
char *data;
unsigned long n;
connection_t *target;
connection_t *source;
source = global_connection_list[src];
// FIXME: check return value
//char *buffer_fetch_data(buffer_t * buf, unsigned long length, unsigned long *rlength)
data = buffer_fetch_data(source->rbuf, source->rbuf->length, &n);
if (data)
{
for (i = 0; i < global_support_connection_num; i++)
{
if (i != src && global_connection_list[i] != NULL)
{
target = global_connection_list[i];
// FIXME: check return value
//int buffer_append_data(buffer_t * buf, char *data, unsigned long length)
buffer_append_data(target->wbuf, data, n);
network_register_write(i);
}
}
free(data);
}
}
转载于:https://blog.51cto.com/carywu/89226
I/O多路复用通信连接select篇相关推荐
- LinuxI/O多路复用转接服务器——select模型实现
LinuxI/O多路复用转接服务器--select模型实现 select函数 函数原型 参数和返回值 fd_set结构体 位操作函数 select实现实现I/O多路复用服务器 实现流程 程序实现 服务 ...
- XBee模块实现QGC与PX4飞控的组网通信连接
本篇博客介绍如何利用XBee模块实现QGC地面站与飞控的通信 一.问题的提出 正如 上一篇博客 指出,PX4飞控原装数传模块(3DR Radio)只能一对一通信,并不能实现多机组网通信,而XBee模块 ...
- 基于C#的Modbus的(NModbus)研究(DTS686电表)——实现TCP通信连接(二)
上一篇说明了基于nmodbus的rtu的连接,本次来介绍一下TCP实现基于modbus的通信,毕竟以后tcp通过ip地址通信才能更简单.和上一篇差不多. 一.首先放出参考的有价值的NModbus网站 ...
- PHP socket多路复用通信demo
PHP socket多路复用通信demo server.php 服务端脚本 function server() {date_default_timezone_set('PRC'); //设置时区set ...
- FPGA通信第三篇--TCP
FPGA通信第三篇–TCP 1 开发目的 本文针对UDP等通信技术不可靠以及速率低的问题,开发基于NIOS软核的TCP通信技术,以实现稳定可靠的高速数据通信. 2 TCP通信技术开发流程 2.1 简介 ...
- C语言网络编程:close或者shutdown断开通信连接
文章目录 前言 close函数介绍 shutdown函数介绍 前言 这里在主要通过实例进行描述close函数在网络编程中的使用 TCP编程模型中客户端或者服务器只要主动通过close发起断开连接的请求 ...
- 多路复用IO: select、sys_select、do_select源码分析
<srsLTE源码学习:逻辑信道多路复用与MAC CE分解pdu_queue.h,pdu_queue.cc> <select用法> <从select函数谈及系统调用原理& ...
- 如何在TIA 博途 WinCC中组态WinCC Runtime Advanced 和 S7 控制器的PROFINET通信连接?
如何在TIA 博途 WinCC中组态WinCC Runtime Advanced 和 S7 控制器的PROFINET通信连接? 前提条件: • WinCC (TIA 博途) 高级版 • STEP 7 ...
- PLC与步科HMI通信连接(RS232)
PLC与步科HMI通信连接_RS232 型号选择 硬件接口说明 PLC主HMI从 HMI主PLC从 型号选择 HMI型号 软件 步科MT4532TE Kinco HMIware 2.5 控制器型号 软 ...
- Noah Mt4跟单系统制作第二篇 Mt4TradeApi连接服务器篇
Noah Mt4跟单系统制作第二篇 Mt4TradeApi连接服务器篇 using System; using Mt4TradeApi;namespace Demo {class Program{st ...
最新文章
- Linux kernel 3.10内核源码分析--进程上下文切换
- python 多条件判断 生成新列_pandas DataFrame 根据多列的值做判断,生成新的列值
- Spark学习之Spark调优与调试(7)
- 纷享销客完成新一轮数亿元融资,持续领跑中国CRM产业发展
- Python修饰符--函数修饰符 “@”
- 关于“我的藏书阁:.NET/数据库应用开发”的几点看法。
- java图片色差_java – JPEG图像的颜色错误
- 对架构师认识的误区有哪些?
- Linux入门-安装篇(Debian 服务器版)
- python类方法和实例方法syntax errors_python新手常犯的17个错误
- 告别低效扫码, Barcode Reader高效解决你批量扫码的困扰
- android art模式 开启,如何开启androidART模式
- Pagehelper获取total错误解决方案
- 微信小程序音频功能开发实(cai)践(keng)
- LabVIEW控制Arduino采集DHT11温湿度数值(进阶篇—4)
- MFC制作Windows画图程序(一)
- SPH算法简介(一): 数学基础
- 不是每个音乐节都值得狂欢,抖音就不一样
- LGBM函数及参数详解
- L1-057 PTA使我精神焕发 (5 分)