Linux平台C语言Socket编程练习之多线程服务器

0x00 要求

采用多线程并发服务器技术,服务器可以同时接受多个客户的请求。具体要求如下:

服务端

1
2
3
接收并显示与之连接的客户端的名称;
接收客户端发来的字符串,显示出来,并对字符串做反转处理,最后将处理后的字符串发回给客户。

客户端

1
2
3
4
5
6
7
根据客户输入的服务器IP地址,向服务器发起建立连接的请求;
接收客户输入的客户端名称,并把该客户端名称发给服务器;
接收客户输入的字符串,将字符串发送给服务器;
接收服务器发回的反转处理后的字符串并显示。继续接受客户输入的字符串,直到用户输入quit时退出。

0x01 代码

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 100
void process_cli(int connfd,struct sockaddr_in client);
void* function(void* arg);
struct ARG
{
int connfd;
struct sockaddr_in client;
};
int main()
{
int listenfd;
int connfd;
pthread_t tid;
socklen_t client_len;
struct ARG *arg;
struct sockaddr_in server;
struct sockaddr_in client;
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket() failed.");
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() failed.");
exit(1);
}
if(listen(listenfd,BACKLOG)==-1)
{
perror("listen() failed.");
exit(1);
}
client_len=sizeof(client);
while(1)
{
if((connfd=accept(listenfd,(struct sockaddr *)&client,&client_len))==-1)
{
perror("accept() failed.");
printf("runed here!\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_create() failed.");
exit(1);
}
}
close(listenfd);
return 0;
}
void process_cli(int connfd,struct sockaddr_in client)
{
ssize_t send_num;
ssize_t recv_num;
char send_buf[MAXDATASIZE];
char recv_buf[MAXDATASIZE];
char client_name[MAXDATASIZE];
char wel_msg[]="[!] Welcome,you can input 'quit' to exit :) ";
printf("[!] You got a connection from client IP: <%s> PORT: <%d> \n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
if((send(connfd,wel_msg,strlen(wel_msg),0))==-1)
{
perror("send welcome messages fail.");
exit(1);
}
if((recv_num=recv(connfd,client_name,MAXDATASIZE,0))==-1)
{
perror("recv() failed.");
close(connfd);
}
if(recv_num==0)
{
printf("[!] Client disconnected.\n");
close(connfd);
}
client_name[recv_num-1]='\0';
printf("[*] Client name: <%s> \n",client_name);
while(1)
{
if((recv_num=recv(connfd,recv_buf,MAXDATASIZE,0))==-1)
{
perror("recv() error.");
exit(1);
}
recv_buf[recv_num-1]='\0';
printf("[*] Received string: '%s' from client: <%s> \n ",recv_buf,client_name);
size_t len=strlen(recv_buf);
for(int i=0; i<(len/2); ++i)
{
char tmp=recv_buf[i];
recv_buf[i]=recv_buf[len-1-i];
recv_buf[len-1-i]=tmp;
}
strcpy(send_buf,recv_buf);
printf("[>] Send reverse string: '%s' to client: <%s> \n",send_buf,client_name);
if((send_num=send(connfd,send_buf,MAXDATASIZE,0))==-1)
{
perror("send() error.");
exit(1);
}
if(!strcmp(send_buf,"tiuq"))
{
printf("[!] Client <%s> have disconnected! \n",client_name);
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);
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 1234
#define MAXDATASIZE 100
int main(int argc,char *argv[])
{
int sockfd;
ssize_t send_num;
ssize_t recv_num;
char wel_msg[MAXDATASIZE];
char cli_name[MAXDATASIZE];
char recv_buf[MAXDATASIZE];
char send_buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server;
if(argc!=2)
{
printf("Usage:%s <IP Adress>\n",argv[0]);
exit(1);
}
if((he=gethostbyname(argv[1]))==NULL)
{
printf("gethostbyname() failed.");
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket() failed.");
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() failed.");
exit(1);
}
if ((recv(sockfd,wel_msg,MAXDATASIZE,0))==-1)
{
perror("[!] Receive welcome message fail.");
exit(1);
}
wel_msg[sizeof(wel_msg)]='\0';
printf("%s\n", wel_msg);
printf("[*] Input client name:");
scanf("%s",cli_name);
if((send_num=send(sockfd,cli_name,MAXDATASIZE,0))==-1)
{
perror("send() error.");
exit(1);
}
while(1)
{
printf("[*] Input string:");
scanf("%s",send_buf);
send_buf[strlen(send_buf)]='\0';
if((send_num=send(sockfd,send_buf,MAXDATASIZE,0))==-1)
{
perror("send() error.");
exit(1);
}
if((recv_num=recv(sockfd,recv_buf,MAXDATASIZE,0))==-1)
{
perror("recv() error.");
exit(1);
}
printf("[>] Received reverse string:%s\n",recv_buf);
if(!strcmp(recv_buf,"tiuq"))
{
break;
}
}
close(sockfd);
return 0;
}

0x02 演示

服务端

多线程服务端.gif

客服端1

多线程客户端1.gif

客户端2

多线程客户端2.gif

0x03 心得

由于pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,不然在使用pthread_create()创建线程时会报:

1
undefined reference to `pthread_create'

类似的错误,由于我是使用的IDE CodeBlocks,所以在Project中找到Build options,在Links setting中添加libpthread.a链接库。

添加库.png

添加状态.gif

如果使用gcc直接使用命令:

1
gcc server.c -o server -lpthread