1、前言
上一篇已经实现了服务器端与客户端之间最基础通信,但存在一些问题,最大的问题是上篇中一个服务器端只能连接一个客户端,如何让一个服务器端可以连接多个客户端呢?利用多进程多线程实现。
2、优化说明
- 优化1: 让服务器程序可以绑定在任何的IP地址上。
- 优化2 :通过程序获取刚建立的socket的客户端的IP地址和端口号。
- 优化3 :连接多个客户端。
- 优化4:允许绑定地址快速重用。
3、几个函数
1、IP地址转换函数
1、 本地字节序转化为网络字节序
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
- af:地址协议族(IPV4:4:AF_INET或IPV6::AF_INET6)
- src:是一个指针,填写点分形式的IP地址
- dest:转换的结果给dst
- 在服务器端Bind()函数绑定IP地址和端口号,客户端connect()函数填充IP地址和端口号时,是将本地字节序转化为网络字节序。
2、网络字节序转化为本地字节序
#include <arpa/inet.h>
int inet_ntop(int af, const char *src, void *dst, socklen_t size);
- af:参数同上
- src:从结构体中读取IP地址。该结构体中有IP地址和端口号等信息。
- dst:读取到的IP地址保存的地址,如果是IPV4,结果为点分形式。
- 结构体大小
- 服务器端读取客户端IP地址和端口号时,需要将网络字节序转化为本地字节序。
2、端口字节序转化
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
3、进程创建函数
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
- 创建一个新的进程,新进程为当前进程的子进程。fork()通过返回值来判断进程是在子进程中,还是父进程中。
- 返回值 = 0,在子进程中; 返回值 <0,创建子进程失败;返回值 >0,在父进程。
- 子进程继承父进程的内容。子进程先结束时,父进程需要及时回收。
4、进程回收函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *wstatus, int options);
- pid=-1 等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数。
- wstatus指定用于保存子进程返回值和结束方式的地址。
- options 指定回收方式,0或WNOHANG。WNOHANG为非阻塞方式。
5、线程创建函数
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
- 参数thread为指向线程标识符的指针。
- attr 为线程属性,NULL表示默认。
- start_routine 线程执行函数。
- arg为传递给执行函数的参数。
多线程实现-服务器端
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <arpa/inet.h>
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.192.143"
#define BACKLOG 5
#define QUIT_STR "quit"
void cli_data_handle(void * arg);
int main()
{
int fd =-1;
struct sockaddr_in sin;
if( (fd = socket(AF_INET,SOCK_STREAM,0)) < 0){
perror("socket");
exit(1);
}
int b_reuse = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
#if 1
sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
if(inet_pton(AF_INET,SERV_IP_ADDR,&sin.sin_addr) != 1){
perror("inet_pton");
exit(1);
}
#endif
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0){
perror("bind error");
exit(1);
}
if(listen(fd,BACKLOG) < 0){
perror("listen");
exit(1);
}
printf("Server starting......OK\n");
int newfd = -1;
#if 0
newfd = accept(fd,NULL,NULL);
if(newfd < 0){
perror("accept");
exit(1);
}
#else
pthread_t tid;
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1){
if((newfd = accept(fd,(struct sockaddr*)&cin,&addrlen)) < 0){
perror("accept");
exit(1);
}
char ipv4_addr[16];
if(! inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin))){
perror("inet_ntop");
exit(1);
}
printf("Client(%s:%d) is connected!\n",ipv4_addr,ntohs(cin.sin_port));
pthread_create(&tid,NULL,(void *)cli_data_handle,(void*)&newfd);
}
close(fd);
}
#endif
void cli_data_handle(void * arg){
int newfd = *(int*)arg;
printf("handler thread:newfd = %d\n",newfd);
int ret = -1;
char buf[BUFSIZ];
while(1){
bzero(buf,BUFSIZ);
do{
ret = read(newfd,buf,BUFSIZ-1);
}while(ret < 0 && EINTR == errno);
if(ret < 0){
perror("read");
exit(1);
}
if(!ret){
break;
}
printf("Receive data(client fd:%d):%s\n",newfd,buf);
if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))){
printf("Client(fd = %d) is exiting!\n",newfd);
break;
}
}
close(newfd);
}
多进程实现 服务器端
1 #include <stdio.h>
2 #include <pthread.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <sys/socket.h>
12 #include <strings.h>
13 #include <netinet/in.h>
14 #include <netinet/ip.h>
15 #include <signal.h>
16 #include <errno.h>
17 #include <arpa/inet.h>
18 #define SERV_PORT 5001
19 #define SERV_IP_ADDR "192.168.192.143"
20 #define BACKLOG 5
21 #define QUIT_STR "quit"
22
23 void cli_data_handle(void * arg);
24
25 void sig_child_handle(int signo){
26 if(SIGCHLD == signo){
27 waitpid(-1, NULL, WNOHANG);
28 }
29
30 }
31 int main()
32 {
33 int fd =-1;
34 struct sockaddr_in sin;
35
36 signal(SIGCHLD,sig_child_handle);
37
38
39 if( (fd = socket(AF_INET,SOCK_STREAM,0)) < 0){
40 perror("socket");
41 exit(1);
42 }
43
44 int b_reuse = 1;
45 setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
46
47
48 bzero(&sin,sizeof(sin));
49 sin.sin_family = AF_INET;
50 sin.sin_port = htons(SERV_PORT);
51
52 #if 1
53 sin.sin_addr.s_addr = htonl(INADDR_ANY);
54 #else
55 if(inet_pton(AF_INET,SERV_IP_ADDR,&sin.sin_addr) != 1){
56 perror("inet_pton");
57 exit(1);
58 }
59 #endif
60
61 if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0){
62 perror("bind error");
63 exit(1);
64 }
65
66 if(listen(fd,BACKLOG) < 0){
67 perror("listen");
68 exit(1);
69 }
70
71 printf("Server starting......OK\n");
72 int newfd = -1;
73 #if 0
74 newfd = accept(fd,NULL,NULL);
75 if(newfd < 0){
76 perror("accept");
77 exit(1);
78 }
79 #else
80
81
82
83 struct sockaddr_in cin;
84 socklen_t addrlen = sizeof(cin);
85 while(1){
86 pid_t pid = -1;
87 if((newfd = accept(fd,(struct sockaddr*)&cin,&addrlen)) < 0){
88 perror("accept");
89 exit(1);
90 }
91
92 if((pid = fork()) < 0){
93 perror("fork");
94 break;
95 }
96 if( 0 == pid)
97 {
98
99 close(fd);
100 char ipv4_addr[16];
101 if(! inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin))){
102 perror("inet_ntop");
103 exit(1);
104 }
105 printf("Client(%s:%d) is connected!\n",ipv4_addr,ntohs(cin.sin_port));
106 cli_data_handle(&newfd);
107 return 0;
108 }
109 else{
110 close(newfd);
111 }
112
113 }
114 }
115 #endif
116 void cli_data_handle(void * arg){
117
118 int newfd = *(int*)arg;
119 printf("child handling process:newfd = %d\n",newfd);
120 int ret = -1;
121 char buf[BUFSIZ];
122 while(1){
123 bzero(buf,BUFSIZ);
124 do{
125 ret = read(newfd,buf,BUFSIZ-1);
126 }while(ret < 0 && EINTR == errno);
127 if(ret < 0){
128 perror("read");
129 exit(1);
130 }
131 if(!ret){
132 break;
133 }
134 printf("Receive data(client fd:%d):%s\n",newfd,buf);
135 if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))){
136 printf("Client(fd = %d) is exiting!\n",newfd);
137 break;
138 }
139 }
140 close(newfd);
141 }
142
143
~
~
~
客户端 执行时输入 ./client serv_ip serv_port
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <arpa/inet.h>
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.192.143"
#define BACKLOG 5
#define QUIT_STR "quit"
void usage(char *s)
{
printf("\n%s serv_ip serv_port",s);
printf("\n\t serv_ip:server ip address");
printf("\n\t serv_port:server port(>5000)\n\n");
}
int main(int argc,char *argv[])
{
int fd = -1;
int port = -1;
if(argc != 3){
usage(argv[0]);
exit(1);
}
struct sockaddr_in sin;
if( (fd = socket(AF_INET,SOCK_STREAM,0)) < 0){
perror("socket");
exit(1);
}
port = atoi(argv[2]);
if(port < 5000){
usage(argv[0]);
exit(1);
}
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if(inet_pton(AF_INET,argv[1],(void*)&sin.sin_addr) != 1){
perror("inet_pton");
exit(1);
}
if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0){
perror("connect ");
exit(1);
}
char buf[BUFSIZ];
while(1){
bzero(buf,BUFSIZ);
if(fgets(buf,BUFSIZ-1,stdin) == NULL){
continue;
}
write(fd,buf,strlen(buf));
if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))){
printf("Client is exiting!\n");
break;
}
}
close(fd);
}
4、编译运行
book@100ask:~/socket$ ./server3
Server starting......OK
Client(192.168.192.143:36066) is connected!
child handling process:newfd = 4
Receive data(client fd:4):welcome
Receive data(client fd:4):147
Receive data(client fd:4):Next change client
Client(192.168.192.143:36068) is connected!
child handling process:newfd = 4
Receive data(client fd:4):hello
Receive data(client fd:4):2021
Receive data(client fd:4):456
Receive data(client fd:4):quit
Client(fd = 4) is exiting!
Client(192.168.192.143:36070) is connected!
child handling process:newfd = 4
Receive data(client fd:4):11111
Receive data(client fd:4):22222
Receive data(client fd:4):quit
book@100ask:~/socket$ ./client 192.168.192.143 5001
welcome
147
Next change client
book@100ask:~/socket$ ./client 192.168.192.143 5001
hello
2021
456
quit
Client is exiting!
book@100ask:~/socket$ ./client 192.168.192.143 5001
11111
22222
quit
Client is exiting!
Client(192.168.192.143:36076) is connected!
handler thread:newfd = 4
Receive data(client fd:4):qwe
Receive data(client fd:4):789
Client(192.168.192.143:36078) is connected!
handler thread:newfd = 5
Receive data(client fd:5):000
Receive data(client fd:5):999
Client(192.168.192.143:36080) is connected!
handler thread:newfd = 6
Receive data(client fd:6):abc
Receive data(client fd:6):711
book@100ask:~/socket$ ./client 192.168.192.143 5001
qwe
789
book@100ask:~/socket$ ./client 192.168.192.143 5001
000
999
book@100ask:~/socket$ ./client 192.168.192.143 5001
abc
711
|