admin管理员组文章数量:1643325
项目场景:
进程通信,服务端连接多个客户端,通过监听客户端套接字,处理客户端数据。
问题描述
TCP连接, 完成三次握手之后,服务端将客户端套接字保存在pollfd结构体中(这个结构体的具体知识看我的上一篇博客
)。客户端起定时器,每隔一秒钟发送一次hello world
给服务端,服务端收到消息后,回复ok
。
客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/time.h> //struct itimeral.setimer()
#include <signal.h> //for singnal()
#include <unistd.h>
int client_fd;
int scok_fd;
char buf[1024]; // 数据传输缓冲区
void Handler(int sig)
{
send(scok_fd, buf, strlen(buf), 0);
char buffer[1024] = {0};
recv(scok_fd, buffer, 1023, 0);
printf("Receive server Data, data is: %s\n", buffer);
}
int main(int argc, char * argv [])
{
struct sockaddr_in remote_addr; //客户端网络地址结构体
struct sockaddr_in my_addr;
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_family = AF_INET; // ip通信
remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //本地ip地址
remote_addr.sin_port = htons(8000); // 端口
//创建套接字 -- ipv4
scok_fd = socket(AF_INET,SOCK_STREAM,0);
if(scok_fd<0)
{
perror("socket error");
return 1;
}
// connect建立和服务器连接
int connectfd = connect(scok_fd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr));
if(connectfd<0)
{
perror("connect error");
return 1;
}
strncpy(buf, "hello world", 1024);
//计时器
signal(SIGALRM,Handler);
struct itimerval timer;
timer.it_value.tv_sec=3; //每隔一秒执行相应的函数
timer.it_value.tv_usec=0;
timer.it_interval.tv_sec=1;
timer.it_interval.tv_usec=0;
int set = setitimer(ITIMER_REAL,&timer,NULL);
if(set<0)
{
perror("timeout");
return -1;
}
while(1);
// 关闭套接字
close(scok_fd);
return 0;
}
服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#define NFDS 100
//创建socket监听套接字
int CreateSocket()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd < 0)
{
perror("socket error");
return 0;
}
printf("CreateSocket success, listenfd:%d\n", listenfd);
struct sockaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(8000);
ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int bindfd = bind(listenfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if(bindfd < 0)
{
perror("bind error");
return 1;
}
printf("bind ser_addr success\n");
listen(listenfd, 10);
return listenfd;
}
//初始化fds结构体数组
void InitFds(struct pollfd *fds)
{
for(int i = 0; i<NFDS; i++)
{
fds[i].fd = -1;
fds[i].events = 0;
fds[i].revents = 0;
}
printf("InitFds success\n");
}
//向fds结构体数组插入一个文件描述符
void InsertFd(struct pollfd *fds, int fd, int flag) //此处flag是为了判断是文件描述符connfd, 还是listenfd,来设置events
{
for(int i=0; i<NFDS; i++)
{
if(fds[i].fd == -1)
{
fds[i].fd = fd;
fds[i].events |= POLLIN;
if(flag)
{
//fds[i].events |= POLLRDHUP;
}
printf("InsertFd:%d success, flag:%d\n", fd, flag);
break;
}
}
}
//从fds结构体数组中删除一个文件描述符
void DeleteFd(struct pollfd *fds, int fd)
{
for(int i=0; i<NFDS; i++)
{
if(fds[i].fd == fd)
{
fds[i].fd = -1;
fds[i].events = 0;
printf("DeleteFd:%d success\n", fd);
break;
}
}
}
//获取一个已经完成三次握手的连接
void GetClientLink(int fd, struct pollfd *fds)
{
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
int connfd = accept(fd, (struct sockaddr*)&cli_addr, &len);
if(connfd<0)
{
perror("accept error");
return;
}
printf("one client link success, socket is %d\n", connfd);
InsertFd(fds, connfd, 1);
}
// 向客户端发送响应
void ChangeEvent(int fd, struct pollfd *fds)
{
for(int i=0; i<NFDS; i++)
{
if(fds[i].fd == fd)
{
fds[i].revents |= POLLOUT;
if(fds[i].events == POLLRDHUP)
{
fds[i].events = POLLIN;
printf("11111111111\n");
}
else
{
fds[i].events = POLLRDHUP;
sleep(3);
fds[i].events = POLLIN;
printf("22222222222\n");
}
printf("ChangeEvent:%d success\n", fd);
break;
}
}
}
//断开用户连接
void UnlinkClient(int fd, struct pollfd *fds)
{
close(fd);
DeleteFd(fds, fd);
printf("One client unlink\n");
}
//处理客户端发来的数据
void DealClientData(int fd, struct pollfd *fds)
{
char buff[1024] = {0};
int n = recv(fd, buff, 1023, 0);
if(n<=0)
{
UnlinkClient(fd, fds);
return;
}
printf("DealClientData, data is: %s\n", buff);
//ChangeEvent(fd, fds);
send(fd, "ok", 2, 0);
}
//处理就绪的文件描述符
void DealFinishFd(struct pollfd *fds, int listenfd)
{
for(int i=0; i<NFDS; i++)
{
if(fds[i].fd==-1)
{
continue;
}
int fd = fds[i].fd;
if(fd==listenfd && fds[i].revents & POLLIN)
{
//获取连接
GetClientLink(fd, fds);
}
else if(fds[i].revents & POLLRDHUP)
{
//断开连接
UnlinkClient(fd, fds);
}
else if(fds[i].revents & POLLIN)
{
//获取客户端数据
DealClientData(fd, fds);
}
}
}
int main()
{
int listenfd = CreateSocket();
//malloc结构体数组
struct pollfd *fds = (struct pollfd*)malloc(sizeof(struct pollfd) * NFDS);
assert(NULL != fds);
//初始化
InitFds(fds);
//插入文件描述符
InsertFd(fds, listenfd, 0);
while(1)
{
int n = poll(fds, NFDS, -1);
if(n <= 0)
{
printf("poll error\n");
continue;
}
DealFinishFd(fds, listenfd);
}
free(fds);
return 0;
}
运行结果
客户端日志
服务端日志
总结
代码注释很详细,大家还是要自己动手去实现,多多摸索啊
版权声明:本文标题:通过socket通信实例深度理解Linux poll机制 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1729357739a1198442.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论