Linux下TCP服务器学习笔记

流程(模型)

1.定义套接字:int
int sfd; // 监听套接字
int cfd;   // 传输套接字
2.创建tcp协议套接字:socket();
int socket(int domain, int type, int protocol);

  • domain: 通信域
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sOzCZetu-1604814834270)(en-resource://database/1190:1)]

  • type: 类型
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HumrEsBk-1604814834274)(en-resource://database/1192:1)]
    TCP:SOCK_STREAM
    UDP:SOCK_DGRAM

  • protocol: 协议
    该参数取决于type值, 大多数 type 像 SOCK_STREAM 和 SOCK_DGRAM 只能使用默认值,要指定默认的protocol,可将该参数设为零

3.绑定服务器IP和端口:
int bind(int socket, sockaddr *addr, int length);

  • 具体的IP和端口设置可看本文最后的代码
 struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}

由于 sa_data[14] 允许 sockaddr 结构引用多种不同类型的网络地址,因此 Linux 提供了一个专用于 IP 的地址结构 sockaddr_in;

4.监听绑定的端口:
int listen(int sockfd, int backlog);

  • backlog 为最大可连接数
流程图


非阻塞IO(non-blocking IO)

  • 旨在提高系统的吞吐量

通过如下函数可将某个句柄fd设为非阻塞状态:
fcntl(fd, F_SETFL, O_NONBLOCK);

多路复用IO(IO multiplexing)

  • 更为高效地检测【可读,可写,是否出错】,让单线程就能同时处理多个网络连接的IO

select/epoll

  • 不断轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程
  • 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading+blocking IO的web server性能更好,可能延迟还更大
select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  • 设置fd_set函数
void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
  • 注意:调用select后,没有相应状态(可读,可写,出错)的fd会被移除出fd_set,因此后面代码会有rset = rfds ;
epoll
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • select当发生事件时,回去遍历所有的fd,效率不高,而epoll会将所有发生事件的fd存到evens里,之后直接遍历evens就行了

PS: EPOLLET (边沿触发),EPOLLLT(水平触发)

TCP服务器代码

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>

#define LISTEN_BACKLOG    1024
#define EPOLL_SIZE        1024

#if 1 /* select/epoll */
int main(void)
{
	int sfd, cfd;
	int sin_size;
	int rcv_len = 0;
	char rcv_buf[128] = {0};
	struct sockaddr_in my_addr, client_addr;
	
	int nready;
	int max_fd;
	fd_set rset, rfds;
	
	sfd = socket(PF_INET, SOCK_STREAM, 0);
	if (sfd < 0)
	{
		perror("socket");
		return -1;
	}
	
	memset(&my_addr, 0, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8000);
	my_addr.sin_addr.s_addr = inet_addr("192.168.240.160");//INADDR_ANY
	
	do {
		if (bind(sfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
		{
			perror("bind");
			break;
		}
		
		if (listen(sfd, LISTEN_BACKLOG) == -1)
		{
			perror("listen");
			break;
		}
#if 0	/* select */
		FD_ZERO(&rfds);
		FD_SET(sfd, &rfds);
		max_fd = sfd;
		
		while (1)
		{
			rset = rfds;
			
			nready = select(max_fd + 1, &rset, NULL, NULL, NULL);
			
			if (nready < 0)
			{
				perror("select");
				continue;
			}

			if (FD_ISSET(sfd, &rset))
			{
				sin_size = sizeof(struct sockaddr_in);
				memset(client_addr, 0, sizeof(client_addr));
				cfd = accept(sfd, (struct sockaddr *)&client_addr, &sin_size);
				if (cfd == -1)
				{
					perror("accept");
					break;
				}
				printf("Client IP:%s\n", inet_ntoa(client_addr.sin_addr));
				
				if (max_fd == FD_SETSIZE)
				{
					printf("Out of range\n");
				}
				
				FD_SET(cfd, &rfds);
				if (cfd > max_fd)
				{
					max_fd = cfd;
				}
				
				if (--nready == 0) continue;
			}
			
			int i;
			for (i = sfd+1; i < max_fd + 1; i++)
			{
				if (FD_ISSET(i, &rset))
				{
					memset(rcv_buf, 0, sizeof(rcv_buf));
					rcv_len = recv(i, rcv_buf, sizeof(rcv_buf), 0);
					if (rcv_len > 0)
					{
						if (send(cfd, rcv_buf, rcv_len, 0) < 0)
						{
							perror("send");
							break;
						}
					}
					else if (rcv_len == 0)
					{
						FD_CLR(i, &rfds);
						close(i);
						break;
					}
					else
					{
						if (errno == EAGAIN || errno == EWOULDBLOCK)
						{
							printf("read all data");
						}
						
						FD_CLR(i, &rfds);
						close(i);
					}	

					if (--nready == 0) break;
				}
			}
		}
#else /* epoll */
		int epoll_fd;
		struct epoll_event ev, events[EPOLL_SIZE] = {0};
		int i;
	
		epoll_fd = epoll_create(1);
		
		ev.events = EPOLLIN;
		ev.data.fd = sfd;
		if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sfd, &ev) == -1)
		{
			perror("epoll_ctl:listen");
			break;
		}
		
		while (1)
		{
			nready = epoll_wait(epoll_fd, events, EPOLL_SIZE, -1);
			
			for (i = 0; i < nready; i++)
			{
				if (events[i].data.fd == sfd)
				{
					sin_size = sizeof(struct sockaddr_in);
					memset(&client_addr, 0, sizeof(client_addr));
					cfd = accept(sfd, (struct sockaddr *)&client_addr, &sin_size);
					if (cfd == -1)
					{
						perror("accept");
						break;
					}
					printf("Client IP:%s\n", inet_ntoa(client_addr.sin_addr));
	
					ev.events = EPOLLIN | EPOLLET;
					ev.data.fd = cfd;
					if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, cfd, &ev) == -1)
					{
						perror("epoll_ctl:accept");
						break;
					}
					
				}
				else
				{
					int clinet_fd = events[i].data.fd;
					
					memset(rcv_buf, 0, sizeof(rcv_buf));
					rcv_len = recv(clinet_fd, rcv_buf, sizeof(rcv_buf), 0);
					if (rcv_len > 0)
					{
						if (send(cfd, rcv_buf, rcv_len, 0) < 0)
						{
							perror("send");
							break;
						}
					}
					else if (rcv_len == 0)
					{
						close(clinet_fd);
						ev.events = EPOLLIN | EPOLLET;
						ev.data.fd = clinet_fd;
						epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clinet_fd, &ev);
						break;
					}
					else
					{
						if (errno == EAGAIN || errno == EWOULDBLOCK)
						{
							printf("read all data");
						}
						ev.events = EPOLLIN | EPOLLET;
						ev.data.fd = clinet_fd;
						epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clinet_fd, &ev);
						close(clinet_fd);
					}	
					

				}
				
			}
		}
#endif
	} while(0);
	
	close(sfd);
	
	return 0;
}
#else
int main(void)
{
	int sfd, cfd;
	int sin_size;
	int rcv_len = 0;
	char rcv_buf[128] = {0};
	struct sockaddr_in my_addr, client_addr;
	
	sfd = socket(PF_INET, SOCK_STREAM, 0);
	if (sfd < 0)
	{
		perror("socket");
		return -1;
	}
	
	memset(&my_addr, 0, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8000);
	my_addr.sin_addr.s_addr = inet_addr("192.168.240.160");//INADDR_ANY
	
	do {
		if (bind(sfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
		{
			perror("bind");
			break;
		}
		
		if (listen(sfd, LISTEN_BACKLOG) == -1)
		{
			perror("listen");
			break;
		}
		
		sin_size = sizeof(struct sockaddr_in);
		cfd = accept(sfd, (struct sockaddr *)&client_addr, &sin_size);
		if (cfd == -1)
		{
			perror("accept");
			break;
		}
		printf("Client IP:%s\n", inet_ntoa(client_addr.sin_addr));
		
		while ((rcv_len = recv(cfd, rcv_buf, sizeof(rcv_buf), 0)) > 0)
		{
			printf("[recv]:%s\n", rcv_buf);
			if (send(cfd, rcv_buf, rcv_len, 0) < 0)
			{
				perror("send");
				break;
			}
			memset(rcv_buf, 0, sizeof(rcv_buf));
		}
	} while(0);
	
	close(cfd);
	close(sfd);
	
	return 0;
}
#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值