案例要求
- 实现一个多进程并发的服务器,可以实现多个客户端的连接。
- 把每个连接的客户端的ip和port输出,而且每个客户端的输入都在服务器输出
- 把客户端的小写转化为大写
声明:因个人能力有限,本文仅是个人的学习记录笔记,有错误之处还望指出
原理图
实现流程
- 新建socket socket(int domain, int type, int protocol)
- 绑定连接(初始化客户端的sockaddr结构体) bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 设置链接上限 listen(int sockfd, int backlog)
- 阻塞等待链接,并且返回客户端的sockfd accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
- 有新的客户端链接就创建线程 fork
当fork的返回值>0,是父进程,负责回收子进程(利用信号)
当fork的返回值<0,是子进程,负责处理数据(利用信号)
代码实现
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include <sys/wait.h>
#include <stdlib.h>
//定义服务器端口
#define SERV_PORT 8888
//定义服务器IP
#define SERV_IP "127.0.0.1"
/* 服务器链接过程
1.建立套接字 --->socket
2.绑定套接字,并且初始化套接字结构体 --->bind
3.设置连接上限 --->listen
4.阻塞等待链接 --->accept
5.读取客户端套接字的数据 --->read
6.处理数据 --->toupper
7.将数据修改 --->write
*/
void *wait_child(int signal){
while( waitpid(0,NULL,WNOHANG)> 0)
return 0;
}
int main(){
//定义客户端和服务器的套接字,循环因子,从缓冲区读到的字节数
int sfd,cfd,i,n;
pid_t pid;
unsigned char ip_buf[sizeof(struct in_addr)];
char buf[BUFSIZ],client_ip[BUFSIZ];
//定义客户端和服务器的套接字结构体
struct sockaddr_in serv_addr,client_addr;
//初始化客户端结构体
bzero(&client_addr,sizeof(client_addr));
//服务器结构体赋值
serv_addr.sin_family = AF_INET;//IPV4模式
serv_addr.sin_port = htons(SERV_PORT);//PORT ---->注意网络字节序
//此处和前面不一样,用inet_pton已经把结构体里面的ip修改
inet_pton(AF_INET,SERV_IP,ip_buf);//IP ---->注意网络字节序
// serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//IP ---->注意网络字节序
//新建socket
sfd = socket(AF_INET,SOCK_STREAM,0);
//绑定链接
bind(sfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
//设置上限
listen(sfd,128);
//阻塞等待
socklen_t client_addr_len;
client_addr_len = sizeof(client_addr);
//循环等待连接,并打印客户端信息
while(1){
cfd = accept(sfd,(struct sockaddr *)&client_addr,&client_addr_len);
//打印客户端的ip和port
//INET——ADDRSTRLEN是操作系统指定的ipv4的str长度的宏
printf("client ip : %s client port :%d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,INET_ADDRSTRLEN),
ntohs(client_addr.sin_port)
);
pid = fork();
if( pid < 0){
perror(" fork");
exit(1);
}
//子进程要操作客户端sock文件描述符,关闭服务器文件描述符
else if( pid == 0){
close(sfd);
break;
}
else{
close(cfd);
signal(SIGCHLD,wait_child);
}
}
if( pid == 0){
//循环读取数据
while(1){
n = read(cfd,buf,sizeof(buf));
//循环处理所有数据
for(i = 0;i < n;i++){
buf[i] = toupper(buf[i]);
}
write(cfd,buf,n);
write(STDOUT_FILENO,buf,n);
}
}
return 0;
}
结果图
因为在同一台虚拟机下故ip一样,但端口号是不同的
而塞过 2021-1-11
关于我:一个就要进入互联网,经历社会毒打的99小伙