首页
最新活动
服务器租用
香港服务器租用
台湾服务器租用
美国服务器租用
日本服务器租用
新加坡服务器租用
高防服务器
香港高防服务器
台湾高防服务器
美国高防服务器
裸金属
香港裸金属服务器
台湾裸金属服务器
美国裸金属服务器
日本裸金属服务器
新加坡裸金属服务器
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
CDN
CDN节点
CDN带宽
CDN防御
CDN定制
行业新闻
官方公告
香港服务器资讯
帮助文档
wp博客
zb博客
服务器资讯
联系我们
关于我们
机房介绍
机房托管
登入
注册
帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
联系客服
服务器资讯
/
香港服务器租用
/
香港VPS租用
/
香港云服务器
/
美国服务器租用
/
台湾服务器租用
/
日本服务器租用
/
官方公告
/
帮助文档
常见的socket函数封装和多进程和多线程实现服务器并发
发布时间:2024-03-03 05:43:45 分类:帮助文档
常见的socket函数封装和多进程和多线程实现服务器并发 常见的socket函数封装和多进程和多线程实现服务器并发 1.常见的socket函数封装2.多进程和多线程实现服务器的并发2.1多进程服务器2.2多线程服务器2.3运行效果 1.常见的socket函数封装 accept函数或者read函数是阻塞函数,会被信号打断,我们不能让它停止,所以我们应该进行一些封装操作。 //wrap.h #ifndef __WRAP_H_ #define __WRAP_H_ #include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
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; else perr_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; else return -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; else return -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
#include
#include
#include
#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
#define MAX_NUM 100 struct 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的状态,可以看出是达到了效果。两个客户端和服务端的通信都是正常的。
上一篇
初学littlefs文件系统
下一篇
测试:腾讯云4核8G服务器支持多少人在线访问?
相关文章
服务器 机柜 线怎么放
vscode服务器连接出现的问题
吃鸡怎么连香港服务器
比特球云盘怎么下载bt种子
2g版的qq空间怎么下载视频吗
centos 7.9每天定期发送最新备份文件到另外一台服务器
大同怎么样制作团购小程序
服务器上数据丢失怎么回事
二级域名 怎么申请吗
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价
7*24H在线售后
高可用资源,安全稳定
1v1专属客服对接
无忧退款试用保障
德讯电讯股份有限公司
电话:00886-982-263-666
台湾总部:台北市中山区建国北路一段29号3楼
香港分公司:九龙弥敦道625号雅兰商业二期906室
服务器租用
香港服务器
日本服务器
台湾服务器
美国服务器
高防服务器购买
香港高防服务器出租
台湾高防服务器租赁
美国高防服务器DDos
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
行业新闻
香港服务器租用
服务器资讯
香港云服务器
台湾服务器租用
zblog博客
香港VPS
关于我们
机房介绍
联系我们
Copyright © 1997-2024 www.hkstack.com All rights reserved.