当前位置: 首页 > news >正文

网站学做糕点的课程全网模板建站系统

网站学做糕点的课程,全网模板建站系统,网站做全景,做网站的公司那家好常见的socket函数封装和多进程和多线程实现服务器并发 1.常见的socket函数封装2.多进程和多线程实现服务器的并发2.1多进程服务器2.2多线程服务器2.3运行效果 1.常见的socket函数封装 accept函数或者read函数是阻塞函数,会被信号打断,我们不能让它停止&a…

常见的socket函数封装和多进程和多线程实现服务器并发

  • 1.常见的socket函数封装
  • 2.多进程和多线程实现服务器的并发
    • 2.1多进程服务器
    • 2.2多线程服务器
    • 2.3运行效果

1.常见的socket函数封装

在这里插入图片描述

accept函数或者read函数是阻塞函数,会被信号打断,我们不能让它停止,所以我们应该进行一些封装操作。

//wrap.h#ifndef __WRAP_H_
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);
int tcp4bind(short port,const char *IP);
#endif

下面是相关函数的实现

//wrap.c#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>void perr_exit(const char *s)
{perror(s);exit(-1);
}int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{int n;again:if ((n = accept(fd, sa, salenptr)) < 0) {if ((errno == ECONNABORTED) || (errno == EINTR))goto again;elseperr_exit("accept error");     }return n;
}int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{int n;if ((n = bind(fd, sa, salen)) < 0)perr_exit("bind error");return n;
}int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{int n;if ((n = connect(fd, sa, salen)) < 0)perr_exit("connect error");return n;
}int Listen(int fd, int backlog)
{int n;if ((n = listen(fd, backlog)) < 0)perr_exit("listen error");return n;
}int Socket(int family, int type, int protocol)
{int n;if ((n = socket(family, type, protocol)) < 0)perr_exit("socket error");return n;
}ssize_t Read(int fd, void *ptr, size_t nbytes)
{ssize_t n;again:if ( (n = read(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}ssize_t Write(int fd, const void *ptr, size_t nbytes)
{ssize_t n;again:if ( (n = write(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}int Close(int fd)
{int n;if ((n = close(fd)) == -1)perr_exit("close error");return n;
}

2.多进程和多线程实现服务器的并发

当有多个客户端向服务器发送数据的时候,我们如何去操作,这就涉及到了我们的多线程和多进程开发了,下面看看如何来实现。

2.1多进程服务器

(1)首先我们想如何通过多进程来实现呢?那么我们得想清楚父子进程分别来干啥,我们可以这样,父进程来获取连接

(2)然后子进程来进行通信发送数据给服务端。

(3)最后我们利用信号的方式来回收子进程,防止出现僵尸进程。

/*多进程实现并发,主进程中使用sigaction函数回收子进程*/
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include "wrap.h"void sighandler(int sig)
{pid_t wpid;//回收子进程while(1){wpid = waitpid(-1, NULL, WNOHANG);if(wpid <= 0){break;}}
}int main()
{int lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));struct sockaddr_in serverAddr;bzero(&serverAddr, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(8888);serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));Listen(lfd, 128);//将SIGCHLD信号阻塞sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGCHLD);sigprocmask(SIG_BLOCK, &mask, NULL);int cfd;pid_t mpid;struct sockaddr_in clientAddr;socklen_t len = sizeof(clientAddr);while(1){cfd = Accept(lfd, (struct sockaddr*)&clientAddr, &len);mpid = fork();if (mpid < 0){perror("fork error:");exit(0);}else if (mpid > 0){close(cfd);//signal(SIGCHLD, sighandler);//注册信号处理函数struct sigaction act;act.sa_handler = sighandler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGCHLD, &act, NULL);//解除对SIGCHLD信号的阻塞sigprocmask(SIG_UNBLOCK, &mask, NULL);}else if(mpid == 0){//子进程中执行消息收发close(lfd);char buf[1024];int nLen; char cIP[16];while(1){memset(buf, 0, sizeof(buf));nLen = Read(cfd, buf, sizeof(buf));if (nLen <= 0){perror("read error:");break;}printf("%s--%d: %s\n", inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, cIP, sizeof(cIP)), ntohs(clientAddr.sin_port), buf);strcat(buf, "---recvied");Write(cfd, buf, strlen(buf));}close(cfd);exit(0);  //子进程退出,防止子进程继续创建子进程}}close(lfd);return 0;
}

2.2多线程服务器

接下来就是多线程服务器如何去实现呢?

我们可以参考上面的多进程开发:
(1)首先我们利用主进程来获取连接。

(2)然后利用子线程来和服务器进行通信给服务器发送数据。

(3)最后设置线程分离属性,任务完成后自动回收子线程。

注意:

(1)线程和进程之间是有不同的,线程的文件描述符时共享的,一旦有一个新的连接过来的时候,所有的通信文件描述符cfd都会改变,但是进程时写时拷贝的,所以进程不会出现这种情况。因此在使用线程开发时,我们要分别给他们开辟空间,这里可以用一个结构体,不同线程使用不同的空间

(2)由于线程的文件描述符是共享的,所以我们不可以关闭父线程的通信文件描述符,这样会导致子线程的通信文件描述符全关闭,导致子线程无法正常通信;而进程程会有计数引用,只会是通信文件描述符的引用次数减1,不会直接全部关闭。

下面是代码:

/*多线程实现并发, 解决多个子线程共享cfd存在的问题*/
#include "wrap.h"
#include <pthread.h>#define MAX_NUM 100struct PthreadInfo
{int cfd;  //若为-1表示可用, 大于0表示已被占用pthread_t threadID;struct sockaddr_in clientAddr;
};
//定义结构体数组,不同的线程访问不同的内存
struct PthreadInfo info[MAX_NUM];//线程执行函数
void* mythread(void* arg)
{struct PthreadInfo* p = (struct PthreadInfo*)arg;char buf[1024];int cfd = p->cfd;ssize_t len;while (1){memset(buf, 0, sizeof(buf));len = Read(cfd, buf, sizeof(buf));if (len <= 0){perror("read error:");close(cfd);p->cfd = -1;  //设置为-1表示该位置可用pthread_exit(NULL);}printf("%s\n", buf);strcat(buf, "---recvied");Write(cfd, buf, strlen(buf));}}void init_info()
{//初始化数组,当cfd = -1时表明这块内存空间可以使用for (size_t i = 0; i < MAX_NUM; i++){info[i].cfd = -1;}
}int find_index()
{int i;for(i = 0; i < MAX_NUM; i++){if (info[i].cfd == -1){break;}}if (i == MAX_NUM){return -1;}return i;
}int main()
{int lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));struct sockaddr_in serverAddr;bzero(&serverAddr, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(8888);serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));Listen(lfd, 128);//初始化init_info();int cfd;int ret;int idx;socklen_t len;struct sockaddr_in client;while (1){len = sizeof(client);bzero(&client, len);cfd = Accept(lfd, (struct sockaddr*)&client, &len);//找数组中空闲的位置idx = find_index();if (idx == -1){close(cfd);continue;}//对空闲位置的元素的成员赋值info[idx].cfd = cfd;memset(&info[idx].clientAddr, &client, len);//创建子线程---该子线程完成对数据的收发ret = pthread_create(&info[idx].threadID, NULL, mythread, &info[idx]);if(ret!=0){printf("create thread error:[%s]\n", strerror(ret));exit(-1);}//设置子线程为分离属性pthread_detach(info[idx].threadID);}close(lfd);return 0;
}

我们在写的时候发现当一些进程完成通信以后,关闭文件描述符,我们的空间是无法进行回收的,这样就会大大浪费空间,因此我们可以写一个函数来返回结束通信的空间位置可利用的空间,来使用这块空间。

int find_index()
{int i;for(i = 0; i < MAX_NUM; i++){if (info[i].cfd == -1){break;}}if (i == MAX_NUM){return -1;}return i;
}

2.3运行效果

下面我们看看效果

1.这是连接的第一个客户端,可以看到通信正常
在这里插入图片描述
2.这是连接的第二个客户端,通信也正常

在这里插入图片描述3.我们用命令来看看连接的状态

在这里插入图片描述
可以看到tcp连接是一个双向的可靠连接,我们连接了两个客户端,所以有四个连接,可以看到都处于ESTABLISHESD的状态,可以看出是达到了效果。两个客户端和服务端的通信都是正常的。

http://www.rdtb.cn/news/1020.html

相关文章:

  • 建设个网站多少钱做电商需要什么条件
  • 关于网站开发市问卷调查舆情服务网站
  • 做商城网站的风险媒体营销
  • 平面设计公司工作室优化落实防控措施
  • 做网站开发的经营范围口碑营销案例有哪些
  • 国内b2b免费网站平台百度竞价推广常用到的工具
  • 企业所得税怎么算2023年seo关键词优化报价
  • 做网站怎样赚卖流量西安专业seo
  • 网站建设供应商百度收录排名查询
  • 威海网站制作网站优化seo是什么
  • 深圳网站建设简介彩虹云商城网站搭建
  • 模板网站如何做seo网站技术制作
  • 自定义导航网站 源码友情链接属于免费推广吗
  • 政元软件做网站成都优化官网公司
  • 网站设计的基本过程网上推广app
  • 网站功能板块怎么做好网站搜索引擎优化
  • 制作网站专业公司哪家好惠州百度seo在哪
  • 推广文案大全排名优化工具
  • 网站设计审美角度网站首页制作
  • 转运公司网站建设新品推广策划方案
  • 做文案的网站企业网站有哪些
  • go语言可以做网站吗网络营销是指
  • 专业设计网址青岛网站开发揭阳seo快速排名
  • 沈阳做企业网站seo优化员
  • 使用js做网站性能测试网上营销
  • 临沂网站建设技术托管湖人今日排名最新
  • 做网站的机构迅雷磁力
  • 文学网站怎样建设网站平台推广
  • 乐清seo搜索引擎优化常用方法
  • 做公司网站页面郑州网站设计