1. 实验目的
- 掌握多线程服务器编程模板;
- 掌握有关线程专用数据TSD程序的编写方法;
2. 实验要求
- 认真阅读和掌握本实验的相关的知识点。
- 上机编写并运行本程序。
- 保存和打印出程序的运行结果,并结合程序进行分析。
3. 实验内容
-
服务器等待客户连接,连接成功后显示客户地址,接着接收该客户的名字并显示,然后接收来自客户的信息(字符串),将该字符串反转,并将结果送回客户。要求服务器具有同时处理多个客户的能力。在每个用户退出时要显示该用户输入的所有信息
-
客户首先与服务器连接,接着接收用户输入客户的名字,将该名字发送给服务器,然后接收用户输入的字符串,并发送给服务器,然后接收服务器返回的经处理后的字符串,并显示之。当用户输入Ctrl+D,终止连接并退出。
4. 实验代码和结果
1. 实验代码
服务端代码:
#include <stdio.h>
#include<stdlib.h>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<string.h>
#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000
void process_cli(int connfd,struct sockaddr_in client);
void savedata(char* recvbuf,char* sendbuf,int len,char* cli_data,char* encli_data,int enlen);
void* function(void* arg);
struct ARG
{
int connfd;
struct sockaddr_in client;
};
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
static void destructor(void *ptr)
{
free(ptr);
}
static void creatkey_once(void)
{
pthread_key_create(&key,destructor);
}
struct ST_DATA
{
int index;
int indexen;
};
void main()
{
int listenfd,connfd;
pthread_t tid;
struct ARG *arg;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t len;
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);
}
len = sizeof(client);
while(1)
{
if((connfd = accept(listenfd, (struct sockaddr *)&client,&len)) == -1)
{
perror("accept() error.\n");
exit(1);
}
arg = (struct ARG *)malloc(sizeof(struct ARG));
arg->connfd = connfd;
memcpy((void *)&arg->client, &client, sizeof(client));
if(pthread_create(&tid, NULL, function, (void*)arg))
{
perror("pthread() error.\n");
exit(1);
}
}
close(listenfd);
}
void process_cli(int connfd,struct sockaddr_in client)
{
int num, j, k, num0, x = 0;
char cli_data[1000], encli_data[1000];
char recvbuf[MAXDATASIZE], recvbuf1[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
int code[10] = {2, 0, 1, 5, 1, 2, 2, 0, 7, 5};
printf("[!] You got a connection from %s.\n", inet_ntoa(client.sin_addr));
num = recv(connfd, cli_name, MAXDATASIZE,0);
if(num == 0)
{
close(connfd);
printf("[!] Client disconnected.\n");
return;
}
cli_name[num-1] = '\0';
printf("[>] Client's name %s.\n", cli_name);
while(num = recv(connfd,recvbuf,MAXDATASIZE,0))
{
recvbuf[num] = '\0';
num0 = num;
printf("[>] Recevied client(%s) message:%s", cli_name,recvbuf);
for(x = 0; x < num; x++)
{
recvbuf1[x] = recvbuf[x];
}
if((j = (num-1)%10) != 0)
{
for(k = 0; k < (10-j); k++)
{
recvbuf[num-1] = '0';
num++;
}
}
int i,m;
for(m = 0; m < num/10; m++)
{
for(i = 0; i < 10; i++)
{
if(recvbuf[10*m+i] >= '0' && recvbuf[10*m+i] <= '9')
{
recvbuf[10*m+i] += code[i];
if(recvbuf[10*m+i] > '9')
{
recvbuf[10*m+i] -= 10;
}
}
else if(recvbuf[10*m+i] >= 'a' && recvbuf[10*m+i] <= 'z')
{
recvbuf[10*m+i] += code[i];
if(recvbuf[10*m+i] > 'z')
{
recvbuf[10*m+i] -= 26;
}
}
else if(recvbuf[10*m+i] >= 'A' && recvbuf[10*m+i] <= 'Z')
{
recvbuf[10*m+i] += code[i];
if(recvbuf[10*m+i] > 'Z')
recvbuf[10*m+i] -= 26;
}
sendbuf[10*m+i] = recvbuf[10*m+i];
}
}
sendbuf[num] = '\0';
savedata(recvbuf1, sendbuf, num0, cli_data, encli_data, num);
send(connfd, sendbuf, strlen(sendbuf), 0);
printf("[>] Send message:%s\n", sendbuf);
if(!strcmp(sendbuf, "sujy122075"))
{
printf("[!] Client (%s) quit.\n\n", cli_name);
close(connfd);
printf("[>] Client(%s) closed connection.\n User's data %s\n", cli_name, cli_data);
printf("[>] Client(%s) closed connection.\n User's encodedata %s\n", cli_name, encli_data);
break;
}
}
close(connfd);
}
void* function(void* arg)
{
struct ARG *info;
info = (struct ARG*)arg;
process_cli(info->connfd,info->client);
free(arg);
pthread_exit(NULL);
}
void savedata(char* recvbuf, char* sendbuf, int len, char* cli_data,char* encli_data, int enlen)
{
struct ST_DATA *data;
pthread_once(&once, creatkey_once);
if((data = (struct ST_DATA *)pthread_getspecific(key)) == NULL)
{
data = (struct ST_DATA *)malloc(sizeof(struct ST_DATA));
pthread_setspecific(key,data);
data->index = 0;
data->indexen = 0;
}
int i = 0;
while(i < len-1)
{
cli_data[data->index++] = recvbuf[i];
i++;
}
cli_data[data->index] = '\0';
i = 0;
while(i < enlen-1)
{
encli_data[data->indexen++] = sendbuf[i];
i++;
}
encli_data[data->indexen] = '\0';
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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<IP Address>\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("[*] Input string to server:");
return(fgets(sendline, MAXDATASIZE, fp));
}
2. 实验结果
服务端运行结果:
客户端运行结果:
5. 实验心得
- 由于一个进程中所有线程共享相同的内存空间,如果多个线程修改相同的内存区就会造成意想不到的结果;
- 明白了在多线程中要避免使用静态变量;
- pthread_key_create函数在进程内部分配一个标志TSD的关键字;
- 在多线程编程时要注意每个参数的使用环境,避免出现不必要的麻烦;
- 在存储客户端发送的数据时有点问题,后面想起来应该直接和发送的原始数据一起存储起来就可以解决这个问题了。
6. 附件
下面给出客户端和服务端的c文件
链接:https://pan.baidu.com/s/1nELIceztR0mqCii24zX9Yg 密码:lfjy