读书人

Linux select兑现的TCP echo

发布时间: 2013-04-07 12:50:11 作者: rapoo

Linux select实现的TCP echo

该文主要是用select实现了一个TCP的echo,客户端连接到服务器端,发送数据,服务器端直接回复原数据给客户端。客户端发送quit则服务器终止。

需要注意的是:

1、每次select前最好都要重新设置一下fd_set

2、不要忘记关闭socket

3、将server_sockfd用fcntl设置为非阻塞

4、select最后一个超时参数,0表示不阻塞,-1表示一直阻塞直到事件发送,还有自定义超时时间。

服务器端代码:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<arpa/inet.h>#include <sys/fcntl.h>void select_test(int port, int backlog) {int rcd;int new_cli_fd;int maxfd;int socklen, server_len;int ci;int watch_fd_list[backlog + 1];for (ci = 0; ci <= backlog; ci++)watch_fd_list[ci] = -1;int server_sockfd;//建立socket,类型为TCP流server_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd == -1) {printf("create server_socket error!\n");exit(1);}//设为非阻塞if (fcntl(server_sockfd, F_SETFL, O_NONBLOCK) == -1) {printf("Set server socket nonblock failed\n");exit(1);}struct sockaddr_in server_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));server_sockaddr.sin_family = AF_INET;server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//设置监听端口server_sockaddr.sin_port = htons(port);server_len = sizeof(server_sockaddr);//绑定rcd = bind(server_sockfd, (struct sockaddr *) &server_sockaddr, server_len);if (rcd == -1) {printf("bind port %d error!\n", ntohs(server_sockaddr.sin_port));exit(1);}//监听rcd = listen(server_sockfd, backlog);if (rcd == -1) {printf("listen error!\n");exit(1);}printf("Server is  waiting on socket=%d \n", server_sockfd);watch_fd_list[0] = server_sockfd;maxfd = server_sockfd;//初始化监听集合fd_set watchset;FD_ZERO(&watchset);FD_SET(server_sockfd, &watchset);struct timeval tv; /* 声明一个时间变量来保存时间 */struct sockaddr_in cli_sockaddr;while (1) {tv.tv_sec = 20;tv.tv_usec = 0; /* 设置select等待的最大时间为20秒*///每次都要重新设置集合才能激发事件FD_ZERO(&watchset);FD_SET(server_sockfd, &watchset);//对已存在到socket重新设置for (ci = 0; ci <= backlog; ci++)if (watch_fd_list[ci] != -1) {FD_SET(watch_fd_list[ci], &watchset);}rcd = select(maxfd + 1, &watchset, NULL, NULL, &tv);switch (rcd) {case -1:printf("Select error\n");exit(1);case 0:printf("Select time_out\n");//超时则清理掉所有集合元素并关闭所有与客户端的socketFD_ZERO(&watchset);for (ci = 1; ci <= backlog; ci++){shutdown(watch_fd_list[ci],2);watch_fd_list[ci] = -1;}//重新设置监听socket,等待链接FD_CLR(server_sockfd, &watchset);FD_SET(server_sockfd, &watchset);continue;default://检测是否有新连接建立if (FD_ISSET(server_sockfd, &watchset)) { //new connectionsocklen = sizeof(cli_sockaddr);new_cli_fd = accept(server_sockfd,(struct sockaddr *) &cli_sockaddr, &socklen);if (new_cli_fd < 0) {printf("Accept error\n");exit(1);}printf("\nopen communication with  Client %s on socket %d\n",inet_ntoa(cli_sockaddr.sin_addr), new_cli_fd);for (ci = 1; ci <= backlog; ci++) {if (watch_fd_list[ci] == -1) {watch_fd_list[ci] = new_cli_fd;break;}}FD_SET(new_cli_fd, &watchset);if (maxfd < new_cli_fd) {maxfd = new_cli_fd;}continue;} else {//已有连接的数据通信//遍历每个设置过的集合元素for (ci = 1; ci <= backlog; ci++) { //dataif (watch_fd_list[ci] == -1)continue;if (!FD_ISSET(watch_fd_list[ci], &watchset)) {continue;}char buffer[128];//接收int len = recv(watch_fd_list[ci], buffer, 128, 0);if (len < 0) {printf("Recv error\n");exit(1);}buffer[len] = 0;//获得客户端的IP地址struct sockaddr_in sockaddr;getpeername(watch_fd_list[ci], (struct sockaddr*) &sockaddr,sizeof(sockaddr));printf("read data [%s] from Client %s on socket %d\n",buffer,inet_ntoa(sockaddr.sin_addr),watch_fd_list[ci]);//发送接收到到数据len = send(watch_fd_list[ci], buffer, strlen(buffer), 0);if (len < 0) {printf("Send error\n");exit(1);}printf("write data [%s] to Client %s on socket %d\n",buffer, inet_ntoa(sockaddr.sin_addr),watch_fd_list[ci]);shutdown(watch_fd_list[ci],2);watch_fd_list[ci] = -1;FD_CLR(watch_fd_list[ci], &watchset);//接收到的是关闭命令if (strcmp(buffer, "quit") == 0) {for (ci = 0; ci <= backlog; ci++)if (watch_fd_list[ci] != -1) {shutdown(watch_fd_list[ci],2);}printf("\nWeb Server Quit!\n");exit(0);}}}break;}}}

#define backlog 5const int port = 8888;

int main() { select_test(port,backlog); return 0;}


客户端代码:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>int client_tcp(char *serIP,in_port_t serPort,char *data);int main(){int port=8888;client_tcp("127.0.0.1",port,"Hello Server1!");client_tcp("127.0.0.1",port,"Hello Server2!");client_tcp("127.0.0.1",port,"Hello Server3!");client_tcp("127.0.0.1",port,"quit");}int client_tcp(char *serIP,in_port_t serPort,char *data){//创建socketint sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sock < 0) {printf("socket Error!");exit(0);}//填充sockaddr_instruct sockaddr_in serAddr;memset(&serAddr, 0, sizeof(serAddr));serAddr.sin_family = AF_INET;serAddr.sin_port = htons(serPort);int rtn = inet_pton(AF_INET, serIP, &serAddr.sin_addr.s_addr);//或者是  serAddr.sin_addr.s_addr=inet_addr(serIP);if (rtn <= 0) {printf("inet_pton Error!");exit(0);}printf("目标服务器地址:%s: %d\n", inet_ntoa(serAddr.sin_addr), ntohs(serAddr.sin_port));printf("     网络层协议:%s\n", serAddr.sin_family == 2 ? "IPv4" : "IPv6");printf("     传输层协议:TCP\n");//链接服务器if (connect(sock, (struct sockaddr *) &serAddr, sizeof(serAddr)) < 0) {printf("connect Error!!\n");exit(0);}//show the other sideprintf("connected Server %s : %d\n", inet_ntoa(serAddr.sin_addr), ntohs(serAddr.sin_port));//发送数据int bufsize = strlen(data);int num = send(sock, data, bufsize, 0);if (num <= 0) {printf("Send Error!!\n");exit(0);}//接收数据fputs("Received: ", stdout);char buffer[100];int n = recv(sock, buffer, 100 - 1, 0);if (n <= 0) {printf("Receive Error!!\n");exit(0);} else {buffer[n] = '\0';printf("%s\n", buffer);}//关闭socketclose(sock);//exit(0);return 0;}


读书人网 >UNIXLINUX

热点推荐