#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MYPORT 1234 // 侦听端口
#define BACKLOG 5 // 最大可连接客户端数量
#define BUF_SIZE 200
int fd_A[BACKLOG]; // 连接的FD数组
int conn_amount; // 当前连接的数量
void showclient()
{
int i;
printf("client amount: %d\n", conn_amount);
for (i = 0; i < BACKLOG; i++)
{
printf("[%d]:%d ", i, fd_A[i]);
}
printf("\n\n");
}
int main(void)
{
int sock_fd, new_fd; // 侦听sock_fd, 新连接new_fd
struct sockaddr_in server_addr; // server address information
struct sockaddr_in client_addr; // connector's address information
socklen_t sin_size;
int yes = 1;
char buf[BUF_SIZE];
int ret;
int i;
//创建侦听Socket
if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Create listening socket error!");
exit(1);
}
//配置侦听Socket
//SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑。
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt error!");
exit(1);
}
server_addr.sin_family = AF_INET; // host byte order
server_addr.sin_port = htons(MYPORT); // short, network byte order
server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));
//绑定新创建的Socket到指定的IP和端口
if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
perror("bind error!");
exit(1);
}
//开始侦听,最大连接数为BACKLOG
if (listen(sock_fd, BACKLOG) == -1)
{
perror("listen error!");
exit(1);
}
printf("listen port %d\n", MYPORT);
//监控文件描述符集合
fd_set fdsr;
//监控文件描述符集合中最大的文件号
int maxsock;
//Select超时返回的时间。
struct timeval tv;
conn_amount = 0;
sin_size = sizeof(client_addr);
maxsock = sock_fd;
while (1)
{
// 初始化文件描述符集合 initialize file descriptor set
FD_ZERO(&fdsr);
// 把Sock_fd加入到文件描述符集合
FD_SET(sock_fd, &fdsr);
// 超时设置30秒
tv.tv_sec = 30;
tv.tv_usec = 0;
// 把活动的socket的句柄加入到文件描述符集合中
for (i = 0; i < BACKLOG; i++)
{
if (fd_A[i] != 0)
{
FD_SET(fd_A[i], &fdsr);
}
}
//Select 函数原型
//int select(nfds, readfds, writefds, exceptfds, timeout)
//nfds: select监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要监视各文件中的
//最大文件号加一
//readfds:select监视的可读文件句柄集合
//writefds:select监视的可写文件句柄集合。
//exceptfds:select监视的异常文件句柄集合。
//timeout:本次select的超时结束时间。
ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
if (ret < 0)
{
perror("select error!");
break;
}
else if (ret == 0)
{
printf("timeout\n");
continue;
}
// 轮询各个文件描述符(socket)
for (i = 0; i < conn_amount; i++)
{
//FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,
// >0表示可读写。
if (FD_ISSET(fd_A[i], &fdsr))
{
//接收数据
ret = recv(fd_A[i], buf, sizeof(buf), 0);
if (ret <= 0) //接收数据出错
{
printf("client[%d] close\n", i);
close(fd_A[i]);
FD_CLR(fd_A[i], &fdsr);
fd_A[i] = 0;
}
else // 数据接收成功
{
//将接收数据的最后一位补0
if (ret < BUF_SIZE)
memset(&buf[ret], '\0', 1);
printf("client[%d] send:%s\n", i, buf);
}
}
}
// 检查是否有新连接进来,如果有新连接进来,接收连接,生成新socket,
//并加入到监控文件描述符集合中。
if (FD_ISSET(sock_fd, &fdsr))
{
//接受连接
new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
if (new_fd <= 0)
{
perror("accept socket error!");
continue;
}
// 将新的连接加入到监控文件描述符集合
if (conn_amount < BACKLOG)
{
fd_A[conn_amount++] = new_fd;
printf("new connection client[%d] %s:%d\n", conn_amount,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if (new_fd > maxsock)
maxsock = new_fd;
}
else
{
printf("max connections arrive, exit\n");
send(new_fd, "bye", 4, 0);
close(new_fd);
break;
}
}
showclient();
}
// 关闭所有连接
for (i = 0; i < BACKLOG; i++)
{
if (fd_A[i] != 0)
{
close(fd_A[i]);
}
}
exit(0);
}
分享到:
相关推荐
`select`函数是Linux提供的一种I/O多路复用机制,它允许程序监控多个文件描述符(如socket)的状态,等待其中一个或多个准备好读写操作。这种机制对于构建高并发、高性能的服务器非常有用。 首先,我们来看`tcp...
### Linux Socket Select 异步聊天实现详解 #### 标题:Linux Socket Select 异步聊天 #### 描述:本文档详细介绍了如何在 Linux 环境下利用 Socket 和 Select 实现一个基本的异步聊天系统。 #### 标签:Linux ...
Socket编程是网络编程的基础,它提供了进程间通信的能力,特别是在分布式系统中,使得不同计算机上的程序可以相互通信。在本主题中,我们将深入探讨如何使用`select`机制和线程池来构建高效的TCP、UDP服务器,以及...
在Linux系统中,`select`函数是用于网络编程中处理多路I/O复用的关键机制之一,它允许程序同时监控多个文件描述符(如网络套接字)的状态变化,而无需实际读取或写入数据。`select`函数在处理高并发连接请求时尤其...
此外,还有错误处理和网络异常的处理,例如使用`setsockopt()`设置socket选项以提高网络连接的可靠性,以及对`select()`、`poll()`或`epoll`等I/O多路复用技术的运用,这些技术可以有效地处理大量并发连接。...
在IT行业中,Linux Socket是进行网络通信的重要工具,尤其对于系统和网络程序员来说,理解和掌握Linux Socket编程至关重要。本示例代码提供了客户端(client)和服务器端(server)的实现,帮助初学者深入理解如何在...
Linux Socket学习(十六).txt和Linux Socket学习(十七).txt可能涉及到了更高级的主题,如多路复用I/O,如select()、poll()或epoll(),这些工具可以帮助程序同时处理多个Socket连接。 Linux Socket学习(十四)....
本文将深入探讨如何在Linux环境下利用`select`实现多socket通信,特别是针对UDP协议的服务端实例。 首先,`select`函数是多路复用I/O模型的一种,它允许程序同时监控多个文件描述符(包括socket),等待它们就绪...
在Linux中,实现异步通信的关键技术之一就是socket编程,尤其是利用`select`函数来监控多个socket的状态变化。`select`函数允许程序员同时监听多个文件描述符,当其中一个或多个描述符准备好读写操作时,`select`...
7. **异步I/O和事件通知**:Linux提供了select()、poll()和epoll()等机制用于处理多个Socket的事件。书中的练习可能会涉及如何使用这些机制实现高效的事件驱动编程。 8. **并发和性能优化**:在高并发场景下,如何...
6. **多路复用技术**:Linux Socket编程中常用的选择器如`select()`、`poll()`或`epoll()`,这些工具能监控多个Socket,等待数据到达或事件发生,从而实现非阻塞I/O或多路复用。 7. **错误处理**:Socket编程中必须...
`select()` 是一个系统调用,在多种操作系统(包括Unix和Linux)中都有提供,用于监控多个文件描述符(例如Socket、文件等)的状态变化。通过 `select()` 可以检测这些描述符是否可以进行读取、写入或发生了异常情况...
### Linux Socket Programming (Linux 套接字编程) #### 知识点概览: 1. **Socket编程基础** - **Socket概念介绍** - **Socket的用途与应用场景** 2. **基本Socket概念** - **Socket域和地址族** - **Socket...
Linux Socket实战编程是深入理解网络通信机制的重要领域,尤其对于从事服务器端开发或者网络编程的IT专业人士来说,它是必备技能之一。本资源包含了Linux Socket实战编程的PDF文档以及配套源码,旨在帮助开发者通过...
- **select()**/**poll()**/**epoll()**:用于多路复用Socket,监控多个Socket的读写就绪状态,提高并发处理能力。 7. **实际应用** - **Web服务器**:HTTP协议基于TCP Socket实现,为用户提供网页浏览服务。 - ...
Linux 下 Socket 连接超时的一种实现方法 在 Linux 环境下,设置套接字(Socket)连接超时是一件非常重要的事情。下面我们将详细介绍 Linux 下 Socket 连接超时的一种实现方法。 首先,需要创建套接字,并将其设置...
Linux Socket编程是构建网络应用程序的基础,它为进程间通信提供了接口,特别适用于网络环境中的数据交换。本资源“实战Linux Socket编程”旨在帮助你深入理解并掌握这一关键技能,尤其对于那些希望在嵌入式领域有所...
- `select()`、`poll()`或`epoll`等函数用于监控多个Socket的状态,实现多路复用,提高程序效率。 - 非阻塞I/O通过设置套接字选项`O_NONBLOCK`,使得发送和接收操作在无数据时不会阻塞。 8. **错误处理**: - 在...