多进程并发服务器
实现流程
1. Socket(); 创建 监听套接字 lfd
2. Bind() 绑定地址结构 Strcut scokaddr_in addr;
3. Listen();
4. while (1) {
cfd = Accpet(); 接收客户端连接请求。
pid = fork();
if (pid == 0){ 子进程 read(cfd) --- 小写->大写 --write(cfd)
close(lfd) 关闭用于建立连接的套接字 lfd
read()
//小写转大写代码
write()
} else if (pid > 0) {
close(cfd); 关闭用于与客户端通信的套接字 cfd
continue;
}
}
5. 子进程:
close(lfd)
read()
小写->大写
write()
父进程:
close(cfd);
注册信号捕捉函数: SIGCHLD
在回调函数中, 完成子进程回收
while (waitpid());
read 函数的返回值:
1. > 0 实际读到的字节数
2. = 0 已经读到结尾(对端已经关闭)【 !重 !点 !】
3. -1 应进一步判断errno的值:
errno = EAGAIN or EWOULDBLOCK: 设置了非阻塞方式 读。 没有数据到达。
errno = EINTR 慢速系统调用被 中断。
errno = “其他情况” 异常。
代码
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "wrap.h"
#define SRV_PORT 9999
void catch_child(int signum)
{
while ((waitpid(0, NULL, WNOHANG)) > 0);
return ;
}
int main(int argc, char *argv[])
{
int lfd, cfd;
pid_t pid;
struct sockaddr_in srv_addr, clt_addr;
socklen_t clt_addr_len;
char buf[BUFSIZ];
int ret, i;
//memset(&srv_addr, 0, sizeof(srv_addr)); // 将地址结构清零
bzero(&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(SRV_PORT);
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
lfd = Socket(AF_INET, SOCK_STREAM, 0);
Bind(lfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
Listen(lfd, 128);
clt_addr_len = sizeof(clt_addr);
while (1) {
cfd = Accept(lfd, (struct sockaddr *)&clt_addr, &clt_addr_len);
pid = fork();
if (pid < 0) {
perr_exit("fork error");
} else if (pid == 0) {
close(lfd);
break;
} else {
struct sigaction act;
act.sa_handler = catch_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
ret = sigaction(SIGCHLD, &act, NULL);
if (ret != 0) {
perr_exit("sigaction error");
}
close(cfd);
continue;
}
}
if (pid == 0) {
for (;;) {
ret = Read(cfd, buf, sizeof(buf));
if (ret == 0) {
close(cfd);
exit(1);
}
for (i = 0; i < ret; i++)
buf[i] = toupper(buf[i]);
write(cfd, buf, ret);
write(STDOUT_FILENO, buf, ret);
}
}
return 0;
}
多线程并发服务器
实现流程
1. Socket(); 创建 监听套接字 lfd
2. Bind() 绑定地址结构 Strcut scokaddr_in addr;
3. Listen();
4. while (1) {
cfd = Accept(lfd, );
pthread_create(&tid, NULL, tfn, (void *)cfd);
pthread_detach(tid); // pthead_join(tid, void **); 新线程---专用于回收子线程。
}
5. 子线程:
void *tfn(void *arg)
{
// close(lfd) 不能关闭。 主线程要使用lfd
read(cfd)
小--大
write(cfd)
pthread_exit((void *)10);
}
代码
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include "wrap.h"
#define MAXLINE 8192
#define SERV_PORT 8000
struct s_info { //定义一个结构体, 将地址结构跟cfd捆绑
struct sockaddr_in cliaddr;
int connfd;
};
void *do_work(void *arg)
{
int n,i;
struct s_info *ts = (struct s_info*)arg;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 可用"[+d"查看
while (1) {
n = Read(ts->connfd, buf, MAXLINE); //读客户端
if (n == 0) {
printf("the client %d closed...\n", ts->connfd);
break; //跳出循环,关闭cfd
}
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
ntohs((*ts).cliaddr.sin_port)); //打印客户端信息(IP/PORT)
for (i = 0; i < n; i++)
buf[i] = toupper(buf[i]); //小写-->大写
Write(STDOUT_FILENO, buf, n); //写出至屏幕
Write(ts->connfd, buf, n); //回写给客户端
}
Close(ts->connfd);
return (void *)0;
}
2.int main(void)
3.{
4. struct sockaddr_in servaddr, cliaddr;
5. socklen_t cliaddr_len;
6. int listenfd, connfd;
7. pthread_t tid;
8.
9. struct s_info ts[256]; //创建结构体数组.
10. int i = 0;
11.
12. listenfd = Socket(AF_INET, SOCK_STREAM, 0); //创建一个socket, 得到lfd
13.
14. bzero(&servaddr, sizeof(servaddr)); //地址结构清零
15. servaddr.sin_family = AF_INET;
16. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IP
17. servaddr.sin_port = htons(SERV_PORT); //指定端口号
18.
19. Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //绑定
20.
21. Listen(listenfd, 128); //设置同一时刻链接服务器上限数
22.
23. printf("Accepting client connect ...\n");
24.
25. while (1) {
26. cliaddr_len = sizeof(cliaddr);
27. connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //阻塞监听客户端链接请求
28. ts[i].cliaddr = cliaddr;
29. ts[i].connfd = connfd;
30.
31. pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
32. pthread_detach(tid); //子线程分离,防止僵线程产生.
33. i++;
34. }
35.
36. return 0;
37.}