赞
踩
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))
listen(sockfd, 8)
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &len);
int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(struct sockaddr_in));//赋值为空 server_addr.sin_family = AF_INET;//IPV4协议 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地地址,如果有多个IP不知道设置为哪个就可以用这个参数。 server_addr.sin_port = htons(2204); if (-1 == bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))) { perror("bind error"); return -1; } if (-1 == listen(sockfd, 8)) { perror("listen error"); return -1; } //accept struct sockaddr_in client_addr; int len = sizeof(struct sockaddr); //clientfd用于收发数据的 int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &len); printf("clientfd: %d, serverfd: %d\n", clientfd, sockfd); while (1) { //receive char buf[128] = {0}; int recv_len = recv(clientfd, buf, 128, 0); printf("accept len: %d", recv_len); if (recv_len == -1){ perror("recv error"); break; } else if (recv_len == 0) { break; } else { printf("recv success\n"); } //send printf("send"); send(clientfd, buf, recv_len, 0); printf("send success"); }
client写法步骤:
1. 调用socket函数,获取一个sockfd(本质上是一个文件描述符),int sockfd = socket(AF_INET, SOCK_STREAM, 0);
2. 调用connect函数建立连接(这一步完成三次握手)
1. 创建一个sockaddr_in结构体变量,通过改结构体设置服务器的IP地址和端口号(这里要调用htonl和htons转换字节序)。
2. 调用connect函数,如果成功返回那么就可以直接用sockfd进行通信了。
3. 使用sockfd进行数据读写。
uint32_t lfd = socket(AF_INET, SOCK_STREAM, 0); if (lfd == -1) { perror("create socket"); exit(-1); } //connect 172.17.71.122 uint32_t dst = 0; inet_pton(AF_INET, "8.141.4.79", &dst); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET;//IPV4协议 server_addr.sin_addr.s_addr = dst; server_addr.sin_port = htons(2204); if (connect(lfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); exit(-1); } while (1) { for (int i = 0; i < 20; ++i) { const char* str = std::to_string(i).c_str(); int write_fd = write(lfd, str, sizeof(str)); char receive[1024] = ""; int len1 = read(lfd, receive, sizeof(receive)); if (len1 < 0) { perror("client read"); exit(-1); } else if (len1 == 0) { printf("server closed!!!"); break; } else { printf(" client read success: %s\n", receive); } sleep(1); } sleep(2); }
select相当于一个代理,去帮我们检测有哪些fd有事件发生,然后返回给我们一个数组,我们需要遍历数组依次处理每个fd。
实现步骤如下:
int ret_code = select(maxfd + 1, &r_set, NULL, NULL, NULL);
这里maxfd+1的目的是为了能够处理maxfd,因为select的实现比较老了。代码如下:
fd_set rf_set, r_set; FD_ZERO(&rf_set);//清空集合 FD_SET(sockfd, &rf_set);//增加sockfd到集合 int maxfd = sockfd;//设置最大fd while (1) { r_set = rf_set; int ret_code = select(maxfd + 1, &r_set, NULL, NULL, NULL); if (FD_ISSET(sockfd, &r_set)) { //accept struct sockaddr_in client_addr; int len = sizeof(struct sockaddr); int client_fd = accept(sockfd, (struct sockaddr*)&client_addr, &len); FD_SET(client_fd, &rf_set);//增加新的fd到fd_set里 maxfd = maxfd > client_fd ? maxfd : client_fd;//更新maxfd } else { for (int i = sockfd + 1; i < maxfd + 1; ++i) { if (FD_ISSET(i, &r_set)) { //receive char buf[128] = {0}; int recv_len = recv(i, buf, 128, 0); printf("accept len: %d", recv_len); if (recv_len == -1){ perror("recv error"); break; } else if (recv_len == 0) { FD_CLR(i, &rf_set); close(i);//记得close,不然会一直在close_wait状态。 break; } else { printf("recv success\n"); } //send printf("send"); send(i, buf, recv_len, 0); printf("send success"); } } } }
改进点:
缺点:
写法代码如下:
// struct pollfd { // int fd; /* File descriptor to poll. */ // short int events; /* Types of events poller cares about. */ // short int revents; /* Types of events that actually occurred. */ // }; struct pollfd fds[1024]; fds[sockfd].fd = sockfd; fds[sockfd].events = POLLIN; int maxfd = sockfd; while (1) { int ret_code = poll(fds, maxfd + 1, -1); if (fds[sockfd].revents & POLLIN) { struct sockaddr_in client_addr; int len = sizeof(struct sockaddr); int client_fd = accept(sockfd, (struct sockaddr*)&client_addr, &len); fds[client_fd].fd = client_fd; fds[client_fd].events = POLLIN; maxfd = maxfd > client_fd ? maxfd : client_fd; } for (int i = sockfd + 1; i < maxfd + 1; ++i) { if (fds[i].revents & POLLIN) { //receive char buf[128] = {0}; int recv_len = recv(i, buf, 128, 0); printf("accept len: %d", recv_len); if (recv_len == -1){ perror("recv error"); break; } else if (recv_len == 0) { fds[i].events = 0; fds[i].fd = -1; close(i); break; } else { printf("recv success\n"); } //send printf("send"); send(i, buf, recv_len, 0); printf("send success"); } } }
改进点:
缺点:
写法步骤:
代码如下:
/*struct epoll_event { uint32_t events; epoll_data_t data; } __EPOLL_PACKED; */ int epoll_fd = epoll_create(1);//这里只要大于0就可以,内部实现改为链表了。 struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = sockfd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev); struct epoll_event epoll_events[1024] = {}; while (1) { int ret_code = epoll_wait(epoll_fd, epoll_events, 1024, -1); for (int i = 0; i < ret_code; ++i) { int connt_fd = epoll_events[i].data.fd; if (connt_fd == sockfd) { struct sockaddr_in client_addr; int len = sizeof(struct sockaddr); int client_fd = accept(sockfd, (struct sockaddr*)&client_addr, &len); ev.events = EPOLLIN; ev.data.fd = client_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev); printf("clientfd: %d\n", client_fd); } else if (epoll_events[i].events & EPOLLIN){ //receive char buf[10] = {0}; int recv_len = recv(connt_fd, buf, 10, 0); printf("accept len: %d", recv_len); if (recv_len == -1){ perror("recv error"); close(connt_fd); continue; } else if (recv_len == 0) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL); close(i); continue; } else { printf("recv success\n"); } //send printf("send"); send(connt_fd, buf, recv_len, 0); printf("send success"); } } }
struct fd_events
{
int fd;
char r_buffer[128];
char w_buffer[128];
int r_idx;
int w_idx;
//这里还可以添加事件以及回调函数。
};
//解决LT模式下无法存数据的问题,每次读完后拼接到buffer后,并修改idx到数组末尾
struct fd_events all_events[1024] = {};
if (epoll_events[i].events & EPOLLIN) {
//xxx
} else if (epoll_events[i].events & EPOLLOUT) {
//xxx
} else if (epoll_events[i].events & EPOLLERR) {
//xxx
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。