在网络通信中,端口是用来区分不同应用程序的标识符。每个应用程序都需要侦听一个端口来接收来自网络的数据传输。对于Linux C 编程而言,实现端口监听是非常重要的一项功能。本文将介绍Linux C编程中常见的端口监听实现方法。
1. 使用socket系统调用
socket(套接字)是linux系统中用于网络通信的重要API之一。通过socket,应用程序可以创建一个网络套接字,并通过这个套接字与网络进行通信。
在Linux C编程中,我们可以使用socket系统调用来创建一个监听套接字,从而实现端口的监听。下面是具体的步骤:
① 创建监听套接字
通过socket()函数创建一个监听套接字。这个函数的之一个参数指定协议族(比如,IPv4或IPv6),第二个参数指定套接字的类型(比如,TCP或UDP),第三个参数指定协议类型(通常为0,表示使用默认协议)。
int listening_socket = socket(AF_INET, SOCK_STREAM, 0);
② 绑定套接字到端口
使用bind()函数将监听套接字绑定到指定端口。这个函数的之一个参数是监听套接字,第二个参数是一个sockaddr结构体指针,用来描述端口和IP地址等信息。需要注意的是,sockaddr结构体的成员必须按照网络字节序进行填充。下面是一个示例:
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address)); // 清空结构体
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port_number); // 将端口号转换成网络字节序
server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 等价于0.0.0.0,表示绑定到所有网卡上
int ret = bind(listening_socket, (struct sockaddr*)&server_address, sizeof(server_address));
③ 开始监听
使用listen()函数将监听套接字设置成被动监听状态,等待客户端发起连接请求。这个函数的之一个参数是监听套接字,第二个参数是等待连接请求的队列长度(通常为5)。
int ret = listen(listening_socket, 5);
④ 处理客户端连接
使用accept()函数接收客户端连接请求,并返回一个新的套接字,用于与客户端进行通信。这个函数的之一个参数是监听套接字,第二个参数是一个指向sockaddr结构体的指针,用于存储客户端的IP地址和端口号。需要注意的是,accept()函数是一个阻塞函数,如果没有客户端连接请求,程序将一直阻塞在这里。
struct sockaddr_in client_address;
socklen_t client_address_len = sizeof(client_address);
int client_socket = accept(listening_socket, (struct sockaddr*)&client_address, &client_address_len);
2. 使用select系统调用
在多并发的网络通信中,使用socket系统调用会带来一个重要的问题:阻塞。如果只有一个客户端连接请求,我们的程序仍然会阻塞在accept()函数处,无法响应其他客户端的请求。为了解决这个问题,可以使用select系统调用。
select()函数可以监听多个文件描述符,当其中有文件描述符就绪(比如可以读写)时,它会返回,并且可以通过FD_ISSET()宏判断哪些文件描述符已经就绪。下面是使用select()函数实现端口监听的基本步骤:
① 创建监听套接字
同样地,通过socket()函数创建一个监听套接字。
② 绑定套接字到端口
同样地,使用bind()函数将监听套接字绑定到指定端口。
③ 开始监听
同样地,使用listen()函数将监听套接字设置成被动监听状态,等待客户端发起连接请求。但是这里我们不会阻塞在accept()函数处。
int max_fd = listening_socket;
fd_set read_set;
fd_set ready_set;
FD_ZERO(&read_set);
FD_SET(listening_socket, &read_set);
while (1) {
ready_set = read_set;
int ret = select(max_fd+1, &ready_set, NULL, NULL, NULL);
if (ret
perror(“select”);
exit(-1);
}
if (FD_ISSET(listening_socket, &ready_set)) {
// 有连接请求,处理连接
} else {
// 有其他数据到达,处理数据
}
}
在循环中,我们先使用FD_ZERO()和FD_SET()宏将监听套接字添加到read_set中,然后不断监听中的文件描述符是否就绪。如果有文件描述符就绪,则使用FD_ISSET()宏判断具体是哪一个文件描述符就绪,然后进行相应的处理。需要注意的是,我们需要保证每次循环都重新赋值ready_set,因为这个会被select()函数修改。
3. 使用epoll系统调用
select系统调用虽然可以避免单线程、单进程网络编程中的阻塞问题,但是在高并况下性能和扩展性较差。为了解决这个问题,Linux内核引入了epoll系统调用。
epoll()函数可以同时监听多个文件描述符,并且只会将就绪的文件描述符加入到epoll实例中。这样,我们可以轻松地实现高并发网络编程。下面是使用epoll()函数实现端口监听的基本步骤:
① 创建epoll实例
使用epoll_create()函数创建一个epoll实例,返回一个文件描述符。这个函数的参数可以用来指定epoll实例中更大可以监听的文件描述符数量。
int epoll_fd = epoll_create(1024);
② 将监听套接字添加到epoll实例中
使用epoll_ctl()函数将监听套接字添加到epoll实例中,用于接收客户端连接请求。这个函数的之一个参数是epoll实例的文件描述符,第二个参数是指令类型(比如添加、删除、修改等),第三个参数是要监听的文件描述符,第四个参数是一个epoll_event结构体,用于指定事件类型和处理方式。
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET; // 监听可读事件,边缘触发模式
event.data.fd = listening_socket; // 监听套接字的文件描述符
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listening_socket, &event);
③ 循环监听套接字文件描述符
使用epoll_wt()函数循环监听epoll实例中的文件描述符,将就绪的文件描述符交给工作线程进行处理。这个函数的参数可以用来指定最多等待多长时间、最多等待多少文件描述符等。
struct epoll_event events[1024];
while (1) {
int n_ready = epoll_wt(epoll_fd, events, 1024, -1);
if (n_ready
perror(“epoll_wt”);
exit(-1);
}
for (int i = 0; i
int fd = events[i].data.fd;
if (fd == listening_socket) {
// 监听套接字就绪,处理连接请求
} else {
// 其他文件描述符就绪,处理数据
}
}
}
需要注意的是,epoll实例提供了两种触发模式:边缘触发模式和水平触发模式。边缘触发模式在文件描述符从未就绪变为就绪的瞬间触发,处理方式更为灵活。而水平触发模式则在文件描述符已经就绪的情况下持续触发,适用于稳定传输数据的场景。
端口监听是Linux C编程中非常重要的一项功能。本文介绍了三种常见的端口监听实现方法,分别是使用socket系统调用、select系统调用和epoll系统调用。读者可以根据自己的需求选择合适的方法进行开发。需要注意的是,网络编程中容易出现阻塞和死锁等问题,需要谨慎调试和处理,保证应用程序的稳定性和可靠性。
相关问题拓展阅读:
- 用C语言在Linux平台上写一个占用tcp8080端口的代码。
- linux虚拟机怎么查看tcp协议端口
用C语言在Linux平台上写一个占用tcp8080端口的代码。
那你要做好久
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd,new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size;
/埋衡/建立TCP套接口
if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf(“create socket error”);
perror(“socket”);
exit(1);
}
//初始化结构体,并绑定8080端口
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8080);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
//绑定套接口
if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1)
{
perror(“bind socket error”);
exit(1);
}
//创建监听套接口
if(listen(sockfd,10)==-1)
{
perror(“listen”);
exit(1);
}
//等待连接
while(1)
{
sin_size = sizeof(struct sockaddr_in);
printf(“server is run.\n”);
//如果建立连接,将产生一个全新的套接字
if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size))==-1)
{
perror(“accept”);
exit(1);
}
printf(“accept success.\n”);
//生成一个子进程来完成和客户端的会话,父进程继续监听
if(!fork())
{
printf(“create new thred success.\n”);
//读取客户端发来的信息
int numbytes;
char buff;
memset(buff,0,256);
if((numbytes = recv(new_fd,buff,sizeof(buff),0))==-1)
{
perror(“recv”);
exit(1);
}
printf(“%s”,buff);
//将从客户端接收到的信息再发回客户端
if(send(new_fd,buff,strlen(buff),0)==-1)
perror(“send”);
close(new_fd);
exit(0);
}
close(new_fd);
}
close(sockfd);
linux虚拟机怎么查看tcp协议端口
在Linux虚拟机中查看tcp协议端口,可以使用netstat命令,它可以显示当前系统中所有正在使用的网络端口以及网络连接状态。
1、查看所有TCP连接:
$ netstat -na | grep “tcp”
2、查看所有TCP监听端口:
$ netstat -na | grep “tcp” | grep “LISTEN”
3、查看指定端口的TCP连接:
$ netstat -na | grep “tcp” | grep “:80”
4、查看所有UDP端口
$ netstat -na | grep “udp”
5、查看指定端口的UDP连接
$ netstat -na | grep “udp” | grep “:53”
关于linux c 端口监听的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。