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

Linux网络编程(下)

 十六、select函数

void FD_ZERO(fd_set *set);           ---清空一个文件描述符集合。fd_set rset;FD_ZERO(&rset);void FD_SET(int fd, fd_set *set);    ---将待监听的文件描述符,添加到监听集合中。FD_SET(3, &rset);FD_SET(5, &rset);FD_SET(6, &rset);void FD_CLR(int fd, fd_set *set);    ---将一个文件描述符从监听集合中移除。FD_CLR(4. &rset);int FD_ISSET(int fd, fd set *set);   ---判断一个文件描述符是否在监听集合中。返回值:在1,不在0FD_ISSET(4, &rset);int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);nfds:监听的所有文件描述符中,最大文件描述符+1readfds:读文件描述符监听集合。     传入、传出参数    writefds:写文件描述符监听集合。    传入、传出参数    NULLexceptfds:异常文件描述符监听集合   传入、传出参数    NULLtimeout:>0:设置监听超时时长。NULL:阻塞监听0:非阻塞监听,轮询返回值:>0:所有监听集合(3个)中,满足对应事件的总数。0:没有满足监听条件的文件描述符-1:errno

十七、select实现多路IO

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<ctype.h>// 封装的错误处理函数
#include"wrap.h"#define SERV_PORT 6666int main(int argc, char* argv[]) {int listenfd, connfd;char buf[BUFSIZ];struct sockaddr_in clie_addr, serv_addr;socklen_t clie_addr_len;listenfd = Socket(AF_INET, SOCK_STREAM, 0);int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));Listen(listenfd, 128);fd_set rset, allset;		//定义读集合,备份集合allsetint ret, maxfd = 0, n, i, j;maxfd = listenfd;			//最大文件描述符FD_ZERO(&allset);			//清空监听集合FD_SET(listenfd, &allset);	//将待监听fd添加到监听集合中while (1) {rset = allset;		//备份ret = select(maxfd + 1, &rset, NULL, NULL, NULL);	//使用select监听if (ret < 0) {perr_exit("select error");}//listenfd满足监听的读事件if (FD_ISSET(listenfd, &rset)) {	clie_addr_len = sizeof(clie_addr);//建立链接,不会阻塞connfd = Accept(listenfd, (struct sockaddr*)&clie_addr, &clie_addr_len);FD_SET(connfd, &allset);	//将新产生的fd,添加到监听集合中,监听数据读事件if (maxfd < connfd) {maxfd = connfd;			//修改maxfd}if (ret == 1) {continue;	//说明select只返回一个,并且是listenfd,后续执行无须执行}}//处理满足读事件的fdfor (i = listenfd + 1; i <= maxfd; i++) {	//找到满足读事件的那个fdif (FD_ISSET(i, &rset)) {	n = Read(i, buf, sizeof(buf));//检测到客户端已经关闭链接if (n == 0) {			Close(i);FD_CLR(i, &allset);		//将关闭的fd,移除出监听集合}else if (n == -1) {perr_exit("read error");}for (j = 0; j < n; j++) {buf[j] = toupper(buf[j]);}write(i, buf, n);write(STDOUT_FILENO, buf, n);}}}Close(listenfd);return 0;
}

十八、select优缺点

缺点:监听上限受文件描述符限制。最大1024轮询fd效率太慢,一个个问,有的可能用不到也被询优点:跨平台。Win、Linux、MacOS、Unix解决:通过自定义数组放入需要被轮询的fd,避免不必要的轮询
// 用client数组解决
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<ctype.h>#include"wrap.h"#define SERV_PORT 6666int main(int argc, char* argv[]) {int i, j, n, maxi;/*自定义数组client,防止遍历1024个文件描述符 FD_SETSIZE默认为1024*/int nready, client[FD_SETSIZE];int maxfd, listenfd, connfd, sockfd;/* #define INET_ADDRSTRLEN 16 */char buf[BUFSIZ], str[INET_ADDRSTRLEN];struct sockaddr_in clie_addr, serv_addr;socklen_t clie_addr_len;fd_set rset, allset;	/*set读事件文件描述符集合allset用来暂存*/listenfd = Socket(AF_INET, SOCK_STREAM, 0);int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));Listen(listenfd, 128);maxfd = listenfd;	/*起初listenfd即为最大文件描述符*/maxi = -1;			/*将来用作client[]的下标,初始值指向0个元素之前下标位置*/for (i = 0; i < FD_SETSIZE; i++) {client[i] = -1;				/*用-1初始化client[]*/}FD_ZERO(&allset);FD_SET(listenfd, &allset);		/*构造select监控文件描述符集*/while (1) {rset = allset;				/*每次循环时都重新设置select监控信号集*/nready = select(maxfd + 1, &rset, NULL, NULL, NULL);if (nready < 0) {perr_exit("select error");}/*说明有新的客户端连接请求*/if (FD_ISSET(listenfd, &rset)) {clie_addr_len = sizeof(clie_addr);//建立连接,不会阻塞connfd = Accept(listenfd, (struct sockaddr*)&clie_addr, &clie_addr_len);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),ntohs(clie_addr.sin_port));for (i = 0; i < FD_SETSIZE; i++) {/*找client[]中没有使用的位置*/if (client[i] < 0) {client[i] = connfd;		/*保存accept返回的文件描述符到client[]里*/break;}}/*达到select能监控的文件个数上限1024*/if (i == FD_SETSIZE) {fputs("too many clients\n", stderr);exit(1);}FD_SET(connfd, &allset);	/*向监控文件描述符集合allset添加新的文件描述符connfd*/if (connfd > maxfd) {maxfd = connfd;			/*select第一个参数需要*/}if (i > maxi) {maxi = i;				/*保证maxi存的总是client[]最后一个元素下标*/}if (--nready == 0) {continue;}}/*检测哪个clients有数据就绪*/for (i = 0; i <= maxi; i++) {if ((sockfd = client[i]) < 0) {continue;}if (FD_ISSET(sockfd, &rset)) {/*当client关闭连接时,服务器端也关闭对应连接*/if ((n = Read(sockfd, buf, sizeof(buf))) == 0) {Close(sockfd);FD_CLR(sockfd, &allset);	/*解除select对此文件描述符的监控*/client[i] = -1;}else if (n > 0) {for (j = 0; j < n; j++) {buf[j] = toupper(buf[j]);}Write(sockfd, buf, n);Write(STDOUT_FILENO, buf, n);if (--nready == 0) {break;}}}}}Close(listenfd);return 0;
}

十九、poll函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);fds:监听的文件描述符[数组]struct pollfd {int fd;          // 待监听的文件描述符short events;    // 待监听的文件描述符对应的监听事件// 取值:POLLIN、POLLOUT、POLLERRshort revents;    // 传入时,给0。如果满足对应事件的话,// 返回非0-->POLLIN、POLLOUT、POLLERR};nfds:监听数组的,实际有效监听个数。timeout:>0: 超时时长。单位:毫秒。-1: 阻塞等待0: 不阻塞返回值:返回满足对应监听事件的文件描述符总个数。优点:自带数组结构。可以将监听事件集合和返回事件集合分离。拓展监听上限。可以超出1024限制。
缺点:不能跨平台。在Linux上无法直接定位满足监听事件的文件描述符,要轮询。read函数返回值:>0:实际读到的字节数=0:socket中,表示对端关闭。close()-1: 如果errno=EINTR 被异常终端。需要重启。如果errno=EAGIN或EWOULDBLOCK以非阻塞方式读数据,但是没有数据。需要再次读。如果errno==ECONNRESET,说明连接被重置,需要c1ose(),移除监听队列。

二十、poll实现服务器

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<poll.h>
#include<errno.h>
#include<ctype.h>#include"wrap.h"#define MAXLINE 80
#define SERV_PORT 8000
#define OPEN_MAX 1024int main(int argc, char* argv[]) {int i, j, maxi, listenfd, connfd, sockfd;int nready;		/*接收poll返回值,记录满足监听事件的fd个数*/ssize_t n;char_buf[MAXLINE], str[INET_ADDRSTRLEN];socklen_t clilen;struct pollfd client[OPEN_MAX];struct sockaddr_in cliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM, 0);int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));Listen(listenfd, 128);client[o].fd = listenfd;	/*要监听的第一个文件描述符存入client[0]*/client[o].events = POLLIN;	/*listenfd监听普通读事件*/for (i = 1; i < OPEN_MAX; i++) {client[i].fd = -1;		/*用-1初始化client[]里剩下元素0也是文件描述符,不能用*/}maxi = 0;					/*client[]数组有效元素中最大元素下标*/while (1) {nready = poll(client, maxi + 1, -1);	/*阻塞监听是否有客户端链接请求*//*listenfd有读事件就绪*/if (client[0].revents & POLLIN) {clilen = sizeof(cliaddr);/*接收客户端请求Accept不会阻塞*/connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));for (i = 1; i < OPEN_MAX; i++) {if (client[i].fd < 0) {client[i].fd = connfd;		/*找到client[]中空闲的位置,存放accept返回的connfd*/break;}}/*达到了最大客户端数*/if (i == OPEN_MAX) {perr_exit("too many clients");}client[i].events = POLLIN;			/*设置刚刚返回的connfd,监控读事件*/if (i > maxi) {maxi = i;						/*更新client[]中最大元素下标*/}if (--nready <= 0) {continue;						/*没有更多就绪事件时,继续回到pol1阻塞*/}}/*前面的if没满足,说明没有listenfd满足,检测client[]看是那个connfd就绪*/for (i = 1; i <= maxi; i++) {if ((sockfd = client[i].fd) < 0) {continue;}if (client[i].revents & POLLIN) {if ((n = Read(sockfd, buf, MAXLINE)) < 0) {/*收到RST标志*/if (errno == ECONNRESET) {printf("client[%d] aborted connectionn", i);Close(sockfd);/*poll中不监控该文件描述符,直接置为-1即可,不用像select中那样移除*/client[i].fd = -1;}else {perr_exit("read error");}}else if (n == 0) {/*说明客户端先关闭链接*/printf("client[%d] closed connection\n", i);Close(sockfd);client[i].fd = -1;}else {for (j = 0; j < n; j++) {buf[j] = toupper(buf[j]);}Writen(sockfd, buf, n);}if (--nready <= 0) {break;}}}}return 0;
}

二十一、设置文件描述符

突破1024文件描述符限制·cat/proc/sys/fs/file-max-->当前计算机所能打开的最大文件个数。受硬件影响。ulimit -a --> 当前用户下的进程,默认打开文件描述符个数。缺省为1024修改:打开sudo vi/etc/security/limits.conf,写入:* soft nofile 65536    -->设置默认值,可以直接借助命令修改。* hard nofile 10000    -->命令修改上限。【注销用户,使其生效】

二十二、epoll函数

epoll:
创建一棵监听红黑树
int epoll_create(int size):size:创建的红黑树的监听节点数量。(仅供内核参考。)返回值:指向新创建的红黑树的根节点的fd。失败:-1 errno操作监听红黑树
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epfd: epoll_create函数的返回值。epfdop: 对该监听红黑数所做的操作。EPOLL_CTL_ADD添加fd到监听红黑树EPOLL_CTL_MOD修改fd在监听红黑树上的监听事件。EPOLL_CTL_DEL将一个fd从监听红黑树上摘下(取消监听)fd: 待监听的fdevent: 本质struct epoll_event 结构体地址events: EPOLLIN/EPOLLOUT/EPOLLERRdata: 联合体int fd; 对应监听事件的fdvoid *ptr;uint32_t u32;uint64_t u64;返回值:成功:0  失败:-1 errno阻塞监听
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);epfd: epoll_create函数的返回值。epfdevents:传出参数,【数组】,满足监听条件的哪些fd结构体。maxevents:数组元素的总个数。1024struct epoll_event evnets[1024];timeout:-1: 阻塞0: 不阻塞>0: 超时时间(毫秒)返回值:>0:满足监听的总个数。可以用作循环上限。0:没有rd满足监听事件I-1:失败。errno


二十三、epoll函数实现多路IO

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#include<ctype.h>#include "wrap.h"#define MAXLINE 8192
#define SERV_PORT 8000
#define OPEN_MAX 5000int main(int argc, char* argv[]) {int i, listenfd, connfd, sockfd;int n, num = 0;ssize_t nready, efd, res;char_buf[MAXLINE], str[INET_ADDRSTRLEN];socklen_t clilen;struct sockaddr_in cliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM, 0);int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));	//端口复用bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));Listen(listenfd, 20);efd = epoll_create(OPEN_MAX);	//创建epoll模型,efd指向红黑树根节点if (efd = -1) {perr_exit("epoll_create error");}struct epoll_event tep, ep[OPEN_MAX];		//tep:epoll_ctl参数 ep[]:epoll_wait参数tep.events = EPOLLIN;tep.data.fd = listenfd;			//指定1fd的监听时间为"读"//将lfd及对应的结构体设置到树上,efd可找到该树res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);if (res == -1) {perr_exit("epoll_ctl error");}while (1) {/*epoll为server阻塞监听事件,ep为struct epoll_event类型数组,OPEN_MAX为数组容量,-1表示永久阻塞*/nready = epoll_wait(efd, ep, OPEN_MAX, -1);if (nready == -1) {perr_exit("epoll_wait error");}for (i = 0; i < nready; i++) {if (!(ep[i].events & EPOLLIN)) {continue;	//如果不是"读"事件,继续循环}//判断满足事件的fd是不是lfdif (ep[i].data.fd == listenfd) {clilen = sizeof(cliaddr);connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);		//接受连接printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));printf("cfd %d---client %d\n", connfd, ++num);tep.events = EPOLLIN;tep.data.fd = connfd;res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);		//加入红黑树if (res = -1) {perr_exit("epoll_ctl error");}}else {//不是lfdsockfd = ep[i].data.fd;n = Read(sockfd, buf, MAXLINE);//读到0,说明客户端关闭链接if (n == 0) {res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);		//将该文件描述符从红黑树摘除if (res = -1) {perr_exit("epoll_ctl error");}Close(sockfd);		//关闭与该客户端的链接printf("client[%d] closed connection(n", sockfd);}else if (n < 0) {//出错perror("read n < 0 error: ");res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);		//摘除节点Close(sockfd);}else {//实际读到了字节数for (i = 0; i < n; i++) {buf[i] = toupper(buf[i]);	//转大写,写回给客户端}Write(STDOUT_FILENO, buf, n);Writen(sockfd, buf, n);}}}}Close(listenfd);return 0;
}

二十四、ET和LT模式

epoll事件模型:
ET模式:
边沿触发--缓冲区剩余未读尽的数据不会导致epoll_wait返回。新的事件(读写)满足,才会触发。LT模式:
水平触发-—默认采用模式。缓冲区剩余未读尽的数据会导致epoll_wait返回。
#include<stdio.h>
#include<stdlib.h>
#include<sys/epoll.h>
#include<errno.h>
#include<unistd.h>#define MAXLINE 10int main(int argc, char* argv[]) {int efd, i;int pfd[2];pid_t pid;char buf[MAXLINE], ch = 'a';pipe(pfd);pid = fork();//子进程写if (pid == 0) {close(pfd[0]);while (1) {//aaaa\nfor (i = 0; i < MAXLINE / 2; i++) {buf[i] = ch;}buf[i - 1] = '\n';ch++;//bbbb\nfor (; i < MAXLINE; i++) {buf[i] = ch;}buf[i - 1] = '\n';ch++;//aaaa\nbbbb\nwrite(pfd[1], buf, sizeof(buf));    //写10个数据sleep(5);}close(pfd[1]);}else if (pid > 0) {//父进程读struct epoll_event event;struct epoll_event resevent[10];	//epoll_wait就绪返回eventint res, len;close(pfd[1]);efd = epoll_create(10);event.events = EPOLLIN | EPOLLET;	//ET边沿触发//event.events = EPOLLIN;			//LT水平触发(默认)event.data.fd = pfd[0];epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event);while(1) {res = epoll_wait(efd, resevent, 10, -1);printf("res %d\n", res);if (resevent[0].data.fd == pfd[0]) {len = read(pfd[0], buf, MAXLINE / 2);    //读5个数据write(STDOUT_FILENO, buf, len);}}close(pfd[0]);close(efd);}else {perror("fork");exit(-1);}return 0;
}//如下图:如果bbbb\n会对epoll_wait()触发就是LT模式,否则是ET模式


二十五、epoll的ET非阻塞模式

ET(edge-triggered):ET是高速工作方式,只支持no-block socket。
应用于预览部分内容,而不需要全部了解struct epoll_event event;
event.events= EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);
int flg = fcntl(cfd, F_GETFL);
flg |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flg); 非阻塞后面的数据读不到就返回了,需要全部读到就用忙轮询epoll优缺点:
优点:高效。突破1024文件描述符。
缺点:不能跨平台。Linux。

二十六、线程池


二十七、TCP和UDP

TCP:
面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。丢包重传。
优点:数据流量稳定、速度稳定、顺序
缺点:传输速度慢。相率低。开销大。
使用场景:数据的完整型要求较高,不追求效率。大数据传输、文件传输。UDP:
无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式。
优点:传输速度块。相率高。开销小。
缺点:数据流量、速度、顺序不稳定。
使用场景:对时效性要求较高场合。稳定性其次。(视频通话)

二十八、recvfrom和sendto函数

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
struct sockaddr *src_addr, socklen_t *addrlen);sockfd:套接字buf:缓冲区地址len:缓冲区大小flags: 0src_addr:(struct sockaddr*) &addr传出。对端地址结构addrlen:传入传出。返回值:成功接收数据字节数。失败:-1errn。0:对端关闭。ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 
const struct sockaddr *dest_addr, socklen_t addrlen);sockfd:套接字buf:存储数据的缓冲区len:数据长度flags:0dest_addr:(struct sockaddr*) &addr传入。目标地址结构addrlen:地址结构长度。返回值:成功写出数据字节数。失败-1,errno

二十九、UDP的CS模型实现

// 服务器端
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>#define SERV_PORT 9527int main(void) {int sockfd;sockfd = socket(AF_INET, SOCK_DGRAM, 0);char buf[1024], clie_IP[BUFSIZ];int ret, i;struct sockaddr_in serv_addr, clie_addr;socklen_t clie_addr_len;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));clie_addr_len = sizeof(clie_addr);while (1) {ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&clie_addr, &clie_addr_len);printf("client 's IP:%s		port:%d\n",inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)),ntohs(clie_addr.sin_port));for (i = 0; i < ret; i++) {buf[i] = toupper(buf[i]);}sendto(sockfd, buf, ret, 0, (struct sockaddr*)&clie_addr, clie_addr_len);}close(sockfd);return 0;
}
// 客户端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>#define SERV_PORT 9527
#define SERV_IP "127.0.0.1"int main(void) {int sockfd;sockfd = socket(AF_INET, SOCK_DGRAM, 0);char buf[BUFSIZ];int ret;struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);while (1) {fgets(buf, sizeof(buf), stdin);sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));// 因为定义了宏,明确了服务器的地址就直接NULLret = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, 0);write(STDOUT_FILENO, buf, ret);}close(sockfd);return 0;
}

三十、本地套接字

// 服务器代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<strings.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<stddef.h>#include "wrap.h"#define SERV_ADDR "serv.socket"int main(void) {int lfd, cfd, len, size, i;struct sockaddr_un servaddr, cliaddr;char buf[4096];lfd = Socket(AF_UNIX, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sun_family = AF_UNIX;strcpy(servaddr.sun_path, SERV_ADDR);// offsetof是用后面参数的首地址减前面参数的首地址len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);	/* servaddr total len */unlink(SERV_ADDR);		/*确保bind之前serv.sock文件不存在,bind会创建该文件*/Bind(lfd, (struct sockaddr*)&servaddr, len);	/*参3不能是sizeof(servaddr)*/Listen(lfd, 20);printf("Accept...\n");while (1) {len = sizeof(cliaddr);	// AF_UNIX大小+108Bcfd = Accept(lfd, (struct sockaddr*)&cliaddr, (socklen_t*)&len);len -= offsetof(struct sockaddr_un, sun_path);		// 得到文件名的长度cliaddr.sun_path[len] = '\0';	/*确保打印时,没有乱码出现*/printf("client bind filename %s\n", cliaddr.sun_path);while ((size = read(cfd, buf, sizeof(buf))) > 0) {for (i = 0; i < size; i++) {buf[i] = toupper(buf[i]);}write(cfd, buf, size);}close(cfd);}close(lfd);return 0;
}// 客户端服务器通过文件通信
// 客户端
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<strings.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<stddef.h>#include "wrap.h"#define SERV_ADDR "serv.socket"
#define CLIE_ADDR "clie.socket"int main(void) {int cfd, len;struct sockaddr_un servaddr, cliaddr;char buf[4096];cfd = Socket(AF_UNIX, SOCK_STREAM, 0);bzero(&cliaddr, sizeof(cliaddr));cliaddr.sun_family = AF_UNIX;strcpy(cliaddr.sun_path, CLIE_ADDR);/*计算客户端地址结构有效长度*/len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path);unlink(CLIE_ADDR);Bind(cfd, (struct sockaddr*)&cliaddr, len);		/*客户端也需要bind,不能依赖自动绑定*/bzero(&servaddr, sizeof(servaddr));		/*构造server地址 */servaddr.sun_family = AF_UNIX;strcpy(servaddr.sun_path, SERV_ADDR);/*计算服务器端地址结构有效长度*/len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);Connect(cfd, (struct sockaddr*)&servaddr, len);while (fgets(buf, sizeof(buf), stdin) != NULL) {write(cfd, buf, strlen(buf));len = read(cfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, len);}close(cfd);return 0;
}
http://www.dtcms.com/a/511306.html

相关文章:

  • Le Cerfav:使用MANUS手套和动作捕捉技术保存传统玻璃制作方法
  • Lua脚本详解
  • 【Block总结】ESSamp,下采样|保留原始图像信息|即插即用
  • 政务服务中心 网站建设html代码模板免费
  • Java日志收集技术
  • Gartner发布2026年十大战略技术趋势
  • 2025无人机在农业生态中的应用实践
  • 在 UOS(统信操作系统,基于 Debian/Ubuntu 体系)上编译 OpenCV 4.10.0
  • High-quality Surface Reconstruction using Gaussian Surfels 论文阅读
  • 百度地图多维检索:自然语言理解的深度搜索实践
  • 软件下载网站地址网站建设好了怎么进行推广
  • 牛客:NC16783拼数
  • UV技术:高效杀菌与精准固化的未来之光
  • PB级数据洪流下的抉择:从大数据架构师视角,深度解析时序数据库选型与性能优化(聚焦Apache IoTDB)
  • 时序数据库TDengine用法
  • 重庆市建设网站公司经济师考试时间2023报名时间
  • 第3章,[标签 Win32] :窗口类06,实例句柄与图标光标
  • 带你了解STM32:PWR电源控制
  • React Hooks完全指南
  • 多线程之ThreadLocal
  • 如何修改root密码
  • (三)React技术核心思想——组件化编程
  • 国外唯美flash个人网站欣赏建设网银
  • 【NVIDIA-H200-3】3节点all-reduce-三节点扩展的性能边界:NVIDIA H200 24 卡集群 all-reduce 通信效率深度剖析
  • D026 vue3+django 论文知识图谱推荐可视化系统 | vue3+vite前端|neo4j 图数据库
  • 桃浦做网站常德经开区网站官网
  • ODOO数据文件(XML、CSV、SQL)是如何转换并加载到 Odoo 数据库
  • ArcGIS JSAPI 学习教程 - 要素图层(FeatureLayer)分类、分组设置可视化样式(ClassBreaksRenderer)
  • 10天!前端用coze,后端用Trae IDE+Claude Code从0开始构建到平台上线
  • [JavaEE初阶] 传输层协议---UDP 相关笔记