今天正式继续网络编程,之前博主没怎么接触过网络编程,所以这部分会写的细一点,预计这部分是需要七天文章对应一星期的写作。希望有人愿意跟我一起学习呀。
🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
⏳全文大约阅读时间: 60min
TCP并发服务器多线程
在上次我们完成了一个基本的TCP的连接的架构,但是会出现很多问题,我们这次就进行一点点的优化,让它接近于真正的服务器的样子。
绑定的任意IP
上次我们书写的是127.0.0.1的地址,因为是本地回环地址,所以可以在任意服务器上运行,但是换一台电脑可能就连不上了,所以我们可以使用更加通用的方式。
sin.sin_addr.s_addr = htonl(INADDR_ANY);
我们可以使用上面的代码来绑定任意ip,其中INADDR_ANY
是一个宏定义,其值为-1,也就是全1,也就是255.255.255.255
也就是全网接收。
网络鉴权
上次我们没有理会accept的后两个参数,导致我们无法得知是谁在访问我们的服务器,我们就不能进行网络鉴权了,其实这个用的比较多,比如之前我写的爬虫有些网站直接封IP就是利用的这个方式。
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
if((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen)) < 0){
perror("accept");
exit(1);
}
上面的方式我们就可以通过cin
来接收到IP地址,从而做判断过滤等操作啦。
多线程编程
之前我们学习过多线程编程,还是要使用的,所以我们进行一下优化,我直接给代码,大家可以看着理解消化一下。
#include <pthread.h>
#include "net.h"
void cli_data_handle(void * arg);
int main(void){
//省略一万行
/*优化3 : 用多线程来处理已建立好连接的*/
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
pthread_t tid;
while(1){
if((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen)) < 0){
perror("accept");
exit(1);
}
char ip4_addr[16];
if(!inet_ntop(AF_INET, (void *)&cin.sin_addr, ip4_addr, sizeof(cin))){
perror("inet_ntop");
exit(1);
}
printf("Client(%s:%d) is connected!\n", ip4_addr, htons(cin.sin_port));
pthread_create(&tid, NULL, (void *)cli_data_handle, (void *)&newfd);
};
return 0;
}
void cli_data_handle(void * arg){
int newfd = *(int *)arg;
printf("handler thread:newfd = %d \n",newfd);
//和newfd进行数据读写
int ret = -1;
char buf[BUFSIZ];
while(1){
bzero(buf, BUFSIZ);
do{
ret = read(newfd, buf, BUFSIZ);
}while(ret < 0 && EINTR == errno);
if(ret < 0){
perror("read");
exit(1);
}
if(!ret){ //对方关闭
break;
}
printf("Receive data : %s\n",buf);
if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){ //用户退出
printf("Client(fd=%d) is exiting!\n",newfd);
break;
}
}
close(newfd);
}
完整的代码在/6.network/1.API/server.c
大家可以尝试运行。
多进程编程
多线程虽然可以满足基本要求,但是因为多线程的稳定性并没有那么高,因为如果进程如果死了就直接全死掉了,所以可以用多进程来写。因为这部分之前进行了学习,所以不会的建议翻一下回炉重造0.0,csdn不允许我放连接那我不放了,专栏里辛苦大家找一下吧。
/*多进程优化*/
while(1){
pid_t pid = -1;
if((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen)) <0){
perror("accept");
break;
}
/*创建一个子进程用于处理已建立的客户的交互数据*/
if((pid = fork()) < 0){
perror("fork");
break;
}
if(0 == pid){//子进程中
close(fd);
char ip4_addr[16];
if(!inet_ntop(AF_INET, (void *)&cin.sin_addr, ip4_addr, sizeof(cin))){
perror("inet_ntop");
exit(1);
}
printf("Client(%s:%d) is connected!\n", ip4_addr, ntohs(cin.sin_port));
cli_data_handle(&newfd);
return 0;
}else{//实际上 pid > 0 父进程中
close(newfd);
}
}
完整代码在6.network/1.API/server_fork.c
中
写在最后
今天依旧还是网络编程,这部分非常重要,所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!