1. 实验目的
- 掌握I/O复用服务器编程模板;
- 掌握有关I/O复用程序的编写方法;
2. 实验要求
- 认真阅读和掌握本实验的相关的知识点。
- 上机编写并运行本程序。
- 保存和打印出程序的运行结果,并结合程序进行分析。
3. 实验内容
谈话程序。双方都可以从终端输入一串字符(以回车结束),通过UDP的方式发送到对方,并显示在对方的终端上。从命令行输入目的地址、目的端口、源地址、源端口。
4. 实验代码和结果
1. 实验代码
服务端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000
typedef struct CLIENT
{
int fd;
char* name;
struct sockaddr_in addr;
char* data;
};
void process_cli(CLIENT* client, char* recvbuf, int len);
void savedata_r(char* recvbuf, char* sendbuf, int len, char* cli_data, char* endata, int enlen);
void main()
{
int i, maxi, maxfd, sockfd;
int nready;
ssize_t n;
fd_set rset, allset;
int listenfd, connectfd;
struct sockaddr_in server;
CLIENT client[FD_SETSIZE];
char recvbuf[MAXDATASIZE];
int sin_size;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket() error.\n");
exit(1);
}
int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("bind() error.\n");
exit(1);
}
if(listen(listenfd, BACKLOG) == -1)
{
perror("listen() error.\n");
exit(1);
}
sin_size = sizeof(struct sockaddr);
maxfd = listenfd;
maxi = -1;
for (i = 0; i < FD_SETSIZE; i++)
{
client[i].fd = -1;
}
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
while(1)
{
struct sockaddr_in addr;
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if(FD_ISSET(listenfd, &rset))
{
if((connectfd = accept(listenfd, (struct sockaddr*)&addr, &sin_size, &sin_size)) == -1)
{
perror("accept() error.\n");
continue;
}
for (i = 0; i < FD_SETSIZE; i++)
{
if(client[i].fd < 0)
{
client[i].fd = connectfd;
client[i].name = new char[MAXDATASIZE];
client[i].addr = addr;
client[i].data = new char[MAXDATASIZE];
client[i].name[0] = '\0';
client[i].data[0] = '\0';
printf("[!] You got a connection from %s.", inet_ntoa(client[i].addr.sin_addr));
break;
}
}
if(i == FD_SETSIZE)
{
printf("[!] too many clients.\n");
}
FD_SET(connectfd, &allset);
if(connectfd > maxfd)
{
maxi = i;
}
if(--nready <= 0)
{
continue;
}
}
for(i = 0; i <= maxi; i++)
{
if((sockfd = client[i].fd) < 0)
{
continue;
}
if(FD_ISSET(sockfd, &rset))
{
if((n = recv(sockfd, recvbuf, MAXDATASIZE, 0)) == 0)
{
close(sockfd);
printf("[>] Client(%s) closed connection. User's data: %s\n", client[i].name, client[i].data);
FD_CLR(sockfd, &allset);
client[i].fd = -1;
free(client[i].name);
free(client[i].data);
}
else
{
process_cli(&client[i], recvbuf, n);
if(--nready <= 0)
{
break;
}
}
}
}
}
close(listenfd);
}
void process_cli(CLIENT* client, char* recvbuf, int len)
{
int num, i, j, n, num1;
char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE], cli_data[MAXDATASIZE], endata[MAXDATASIZE];
int code[10]={2, 0, 1, 5, 1, 2, 2, 0, 7, 5};
recvbuf[len-1] = '\0';
if(strlen(client->name) == 0)
{
memccpy(client->name, recvbuf, len);
printf("[>] Client's name is %s.\n", client->name);
return;
}
printf("[>] Received client(%s) message: %s\n", client->name, recvbuf);
printf("[!] You got a connection from: %s, the port is: %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
num = recv(connfd, cli_name, MAXDATASIZE, 0);
if(num == 0)
{
close(connfd);
printf("[!] Client disconnected.\n");
return;
}
num1 = num;
cli_name[num - 1] = '\0';
printf("Client's name is %s.\n",cli_name);
while (num = recv(connfd, recvbuf, MAXDATASIZE, 0))
{
recvbuf[num] = '\0';
printf("[>] Received client (%s) message: %s",cli_name,recvbuf);
if((i = (num - 1) % 10) != 0)
{
for(j = 0; j < (10 - i); j++)
{
recvbuf[num - 1] = '0';
num++;
}
}
for(n = 0; n < num / 10; n++)
{
for(i = 0; i < 10; i++)
{
if(recvbuf[10 * n + i] >= '0' && recvbuf[10 * n + i] <= '9')
{
recvbuf[10 * n + i] += code[i];
if(recvbuf[10 * n + i]>'9')
{
recvbuf[10 * n + i] -= 10;
}
}
else if(recvbuf[10 * n + i] >= 'a' && recvbuf[10 * n + i] <= 'z')
{
recvbuf[10 * n + i] += code[i];
if(recvbuf[10 * n + i] > 'z')
{
recvbuf[10 * n + i] -= 26;
}
}
else if(recvbuf[10 * n + i] >= 'A' && recvbuf[10 * n + i] <= 'Z')
{
recvbuf[10 * n + i] += code[i];
if(recvbuf[10 * n + i] > 'Z')
{
recvbuf[10 * n + i] -= 26;
}
}
sendbuf[10 * n + i] = recvbuf[10 * n + i];
}
}
sendbuf[num - 1] = '\0';
savedata_r(recvbuf, sendbuf, num1, cli_data, endata, num);
send(connfd, sendbuf, strlen(sendbuf),0);
printf("[>] Send message:%s\n",sendbuf);
if(!strcmp(sendbuf, "sujy122075"))
{
printf("[>] Client (%s) quit.\n",cli_name);
close(connfd);
break;
}
}
close(connfd);
printf("client(%s) closed connection.\nuser's data:%s\n", cli_name, cli_data);
printf("client(%s) closed connection.\nuser's endata:%s\n", cli_name, endata);
}
void savedata_r(char* recvbuf, char* sendbuf, int len, char* cli_data, char* endata, int enlen)
{
int i = 0;
while(i < len - 1)
{
cli_data[data->index++] = recvbuf[i];
i++;
}
i = 0;
while(i < len - 1)
{
endata[data->index++] = sendbuf[i];
i++;
}
cli_data[data->index] = '\0';
}
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#define PORT 1234
#define MAXDATASIZE 1000
void process(FILE *fp,int sockfd);
char* getMessage(char* sendline,int len,FILE *fp);
int main(int argc,char *argv[])
{
int sockfd;
struct hostent* he;
struct sockaddr_in server;
if(argc != 2)
{
printf("Usage:%s\n",argv[0]);
exit(1);
}
if((he = gethostbyname(argv[1])) == NULL)
{
printf("gethostbyname() error\n");
exit(1);
}
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
printf("socket() error\n");
exit(1);
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *((struct in_addr *)he->h_addr);
if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
printf("connect() error\n");
exit(1);
}
process(stdin,sockfd);
close(sockfd);
return 0;
}
void process(FILE *fp,int sockfd)
{
char sendline[MAXDATASIZE],recvline[MAXDATASIZE];
int num;
printf("[!] Connected to server.\n");
printf("[*] please input client's name:");
if(fgets(sendline,MAXDATASIZE,fp) == NULL)
{
printf("[!] Exit.\n");
return;
}
send(sockfd, sendline, strlen(sendline), 0);
while(getMessage(sendline, MAXDATASIZE,fp) != NULL)
{
send(sockfd, sendline, strlen(sendline), 0);
if((num = recv(sockfd, recvline, MAXDATASIZE, 0)) == 0)
{
printf("Server terminated.\n");
return;
}
recvline[num] = '\0';
printf("[>] Server Message:%s\n",recvline);
if(!strcmp(recvline, "sujy122075"))
{
close(sockfd);
break;
}
}
printf("[!] Exit.\n");
}
char* getMessage(char* sendline, int len, FILE* fp)
{
printf("[*] Please input string to server:");
return(fgets(sendline, MAXDATASIZE, fp));
}
2. 实验结果
服务端运行结果:
客户端运行结果:
5. 实验心得
- 学会了I/O复用编程;
- I/O复用中select函数允许进程指示内核等待多个事件中的任意一个发生,并仅在一个或多个事件发生或经过指定的时间时才唤醒进程;
- FD_SET(int fd, fd_set* fdset)的含义是将fdset描述字的第fd位设为1;
- I/O复用在网络编程中的作用很重要,需要加强理解,并多动手敲代码。
6. 附件
下面给出客户端和服务端的c文件
链接:https://pan.baidu.com/s/15QeePyRZIig3bfx6PFseiQ 密码:2phc