威海网站建设哪家好,中国营销型网站,网站运营优化方案,北京网站建设 网络推广文章目录#xff1a;
一#xff1a;wrap常用函数封装
wrap.h
wrap.c
server.c封装实现
client.c封装实现
二#xff1a;多进程process并发服务器
server.c服务器
实现思路
代码逻辑
client.c客户端
三#xff1a;多线程thread并发服务器
server.c服务器
实现…文章目录
一wrap常用函数封装
wrap.h
wrap.c
server.c封装实现
client.c封装实现
二多进程process并发服务器
server.c服务器
实现思路
代码逻辑
client.c客户端
三多线程thread并发服务器
server.c服务器
实现思路
代码逻辑
client.c客户端 一wrap常用函数封装 wrap.h //声明了一些网络编程中常用的函数//所有的函数都放在条件编译的代码块中这样只有在编译时定义了 __WRAP_H_ 这个宏#ifndef __WRAP_H_#define __WRAP_H_//perr_exit用来处理错误并退出程序void perr_exit(const char *s);//Accept用于接受一个TCP连接请求//它接受一个文件描述符 fd一个指向 struct sockaddr 结构的指针 sa以及一个指向 socklen_t 类型的指针 salenptrint Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);//Bind用于将一个网络地址包括IP地址和端口号绑定到一个文件描述符上//它接受一个文件描述符 fd一个指向 struct sockaddr 结构的指针 sa以及 socklen_t 类型的 salenint Bind(int fd, const struct sockaddr *sa, socklen_t salen);//Connect用于建立与远程主机的TCP连接//它接受一个文件描述符 fd一个指向 struct sockaddr 结构的指针 sa以及 socklen_t 类型的 salenint Connect(int fd, const struct sockaddr *sa, socklen_t salen);//Listen用于在服务器端创建一个TCP监听队列//它接受一个文件描述符 fd 和一个指定的最大连接等待数 backlogint Listen(int fd, int backlog);//Socket用于创建一个新的套接字//它接受一个地址族 family一个套接字类型 type以及一个协议编号 protocolint Socket(int family, int type, int protocol);//Read用于从文件中读取数据//它接受一个文件描述符 fd一个指向要读取数据的缓冲区的指针 ptr以及要读取的最大字节数 nbytesssize_t Read(int fd, void *ptr, size_t nbytes);//Write用于将数据写入文件//它接受一个文件描述符 fd一个指向要写入数据的缓冲区的指针 ptr以及要写入的字节数 nbytesssize_t Write(int fd, const void *ptr, size_t nbytes);//Close用于关闭文件//它接受一个文件描述符 fdint Close(int fd);//Readn用来读取指定数量的字节//它接受一个文件描述符 fd一个指向要读取数据的缓冲区的指针 vptr以及要读取的字节数 nssize_t Readn(int fd, void *vptr, size_t n);//Writen用来写入指定数量的字节//它接受一个文件描述符 fd一个指向要写入数据的缓冲区的指针 vptr以及要写入的字节数 nssize_t Writen(int fd, const void *vptr, size_t n);//my_read用来读取数据到一个字符数组中//它接受一个文件描述符 fd一个指向要读取数据的缓冲区的指针 ptrssize_t my_read(int fd, char *ptr);//Readline用来读取一行数据//它接受一个文件描述符 fd一个指向要读取数据的缓冲区的指针 vptr以及要读取的最大字节数 maxlenssize_t Readline(int fd, void *vptr, size_t maxlen);#endif wrap.c //这个头文件包含了标准库的函数和变量包括用于内存分配、输入/输出、错误处理等功能的函数和变量#include stdlib.h
//这个头文件包含了标准输入/输出库的函数和变量包括用于文件操作、标准输入/输出等功能的函数和变量 #include stdio.h
//这个头文件包含了用于Unix和类Unix系统中的函数和变量包括与进程控制、系统调用等相关的函数和变量 #include unistd.h
//这个头文件包含了用于错误处理的函数和变量包括用于表示错误码的宏和全局变量#include errno.h
//这个头文件包含了用于网络编程的函数和变量包括用于创建、操作套接字等功能的函数和变量#include sys/socket.h //perr_exit用来处理错误并退出程序void perr_exit(const char *s){perror(s);exit(-1);}//Accept用于接受一个TCP连接请求 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;}//Bind用于将一个网络地址包括IP地址和端口号绑定到一个文件描述符上 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;}//Connect用于建立与远程主机的TCP连接 int Connect(int fd, const struct sockaddr *sa, socklen_t salen){int n;n connect(fd, sa, salen);if (n 0) {perr_exit(connect error);}return n;}//Listen用于在服务器端创建一个TCP监听队列 int Listen(int fd, int backlog){int n;if ((n listen(fd, backlog)) 0)perr_exit(listen error);return n;}//Socket用于创建一个新的套接字 int Socket(int family, int type, int protocol){int n;if ((n socket(family, type, protocol)) 0)perr_exit(socket error);return n;}//Read用于从文件中读取数据 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;}//Write用于将数据写入文件 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;}//Close用于关闭文件 int Close(int fd){int n;if ((n close(fd)) -1)perr_exit(close error);return n;}//Readn用来读取指定数量的字节 /*参三: 应该读取的字节数 读 N 个字节*/ //socket 4096 readn(cfd, buf, 4096) nleft 4096-1500ssize_t Readn(int fd, void *vptr, size_t n){size_t nleft; //usigned int 剩余未读取的字节数ssize_t nread; //int 实际读到的字节数char *ptr;ptr vptr;nleft n; //n 未读取字节数while (nleft 0) {if ((nread read(fd, ptr, nleft)) 0) {if (errno EINTR)nread 0;elsereturn -1;} else if (nread 0)break;nleft - nread; //nleft nleft - nread ptr nread;}return n - nleft;}//Writen用来写入指定数量的字节 ssize_t Writen(int fd, const void *vptr, size_t n){size_t nleft;ssize_t nwritten;const char *ptr;ptr vptr;nleft n;while (nleft 0) {if ( (nwritten write(fd, ptr, nleft)) 0) {if (nwritten 0 errno EINTR)nwritten 0;elsereturn -1;}nleft - nwritten;ptr nwritten;}return n;}//my_read用来读取数据到一个字符数组中 static ssize_t my_read(int fd, char *ptr){static int read_cnt;static char *read_ptr;static char read_buf[100];if (read_cnt 0) {again:if ( (read_cnt read(fd, read_buf, sizeof(read_buf))) 0) { //hello\nif (errno EINTR)goto again;return -1;} else if (read_cnt 0)return 0;read_ptr read_buf;}read_cnt--;*ptr *read_ptr;return 1;}//Readline用来读取一行数据 /*readline读一行 --- fgets*/ //传出参数 vptrssize_t Readline(int fd, void *vptr, size_t maxlen){ssize_t n, rc;char c, *ptr;ptr vptr;for (n 1; n maxlen; n) {if ((rc my_read(fd, c)) 1) { //ptr[] hello\n*ptr c;if (c \n)break;} else if (rc 0) {*ptr 0;return n-1;} elsereturn -1;}*ptr 0;return n;
} 利用封装函数 联合编译server.c 和 wrap.c 生成 server、 联合编译 client.c 和 wrap.c 生成 client server.c封装实现 #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 wrap.h#define SERV_PORT 6666int main(void)
{int sfd, cfd;int len, i;char buf[BUFSIZ], clie_IP[BUFSIZ];struct sockaddr_in serv_addr, clie_addr;socklen_t clie_addr_len;/*1.socket函数:创建用于建立连接的socket,返回的文件描述符存入link_fd*/sfd Socket(AF_INET, SOCK_STREAM, 0);int opt 1;//设置套接字选项:文件描述符,要设置的选项是套接字的选项,要设置的选项是“重用地址”选项,包含了要设置的选项的值,选项值的长度setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));bzero(serv_addr, sizeof(serv_addr)); //将指定内存区域的内容初始化为0 serv_addr.sin_family AF_INET; //IPv4 serv_addr.sin_addr.s_addr htonl(INADDR_ANY); //获取本机任意有效IPserv_addr.sin_port htons(SERV_PORT); //转为网络字节序的 端口号/*2.bind函数:绑定服务器端的socket绑定地址结构IPport)*/Bind(sfd, (struct sockaddr *)serv_addr, sizeof(serv_addr));/*3.listen函数:设定监听(连接)上线*/Listen(sfd, 2); printf(wait for client connect ...\n);clie_addr_len sizeof(clie_addr_len);/*4.accept函数:阻塞等待客户端建立连接*/cfd Accept(sfd, (struct sockaddr *)clie_addr, clie_addr_len);/*建立连接后打印客户端的IP和端口号 获取客户端地址结构*/printf(cfd ----%d\n, cfd);printf(client 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));while (1) {//5. read(fd) 读socket获取客户端数据len Read(cfd, buf, sizeof(buf));Write(STDOUT_FILENO, buf, len);//6. 小--大写 toupper()for (i 0; i len; i)buf[i] toupper(buf[i]);//7. write(fd)Write(cfd, buf, len); }//8. close()Close(sfd);Close(cfd);return 0;
} client.c封装实现 #include stdio.h
#include unistd.h
#include string.h
#include sys/socket.h
#include arpa/inet.h#include wrap.h#define SERV_IP 127.0.0.1
#define SERV_PORT 6666int main(void)
{int sfd, len;struct sockaddr_in serv_addr;char buf[BUFSIZ]; //1. socket() 创建socketsfd Socket(AF_INET, SOCK_STREAM, 0); bzero(serv_addr, sizeof(serv_addr)); //将指定内存区域的内容初始化为0 serv_addr.sin_family AF_INET; //IPv4 inet_pton(AF_INET, SERV_IP, serv_addr.sin_addr.s_addr); //本地字节序string IP --- 网络字节序serv_addr.sin_port htons(SERV_PORT); //转为网络字节序的 端口号//2. connect(); 与服务器建立连接Connect(sfd, (struct sockaddr *)serv_addr, sizeof(serv_addr));while (1) {fgets(buf, sizeof(buf), stdin);//3. write() 写数据到 socketint r Write(sfd, buf, strlen(buf)); printf(Write r %d\n, r);//4. read() 读转换后的数据len Read(sfd, buf, sizeof(buf));printf(Read len %d\n, len);//5. 显示读取结果Write(STDOUT_FILENO, buf, len);}//6. close()Close(sfd);return 0;
} read 函数的返回值 read 函数的返回值1. 0 实际读到的字节数2. 0 已经读到结尾对端已经关闭【 重 点 】3. -1 应进一步判断errno的值errno EAGAIN or EWOULDBLOCK: 设置了非阻塞方式 读。 没有数据到达。 errno EINTR 慢速系统调用被 中断。errno “其他情况” 异常。 二多进程process并发服务器
server.c服务器 实现思路 使用多进程并发服务器时要考虑以下几点1.父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)2.系统内创建进程个数(与内存大小相关)3.进程创建过多是否降低整体服务性能(进程调度) 1. Socket(); 创建 监听套接字 lfd2. Bind() 绑定地址结构 Strcut scokaddr_in addr;3. Listen(); 4. while (1) {cfd Accpet(); 接收客户端连接请求。pid fork();if (pid 0){ 子进程 read(cfd) --- 小-》大 --- write(cfd)close(lfd) 关闭用于建立连接的套接字 lfdread()小--大write()} else if pid 0 { close(cfd); 关闭用于与客户端通信的套接字 cfd contiue;}}5. 子进程close(lfd)read()小--大write() 父进程close(cfd);注册信号捕捉函数 SIGCHLD在回调函数中 完成子进程回收while waitpid(); 代码逻辑 #include stdio.h
#include string.h
#include netinet/in.h
#include arpa/inet.h
#include signal.h
#include sys/wait.h
#include ctype.h
#include unistd.h#include wrap.h#define MAXLINE 8192
#define SERV_PORT 8000//在回调函数中 完成子进程回收void do_sigchild(int num){while (waitpid(0, NULL, WNOHANG) 0);}int main(void)
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;char buf[MAXLINE];char str[INET_ADDRSTRLEN];int i, n;pid_t pid;struct sigaction newact;//5.父进程newact.sa_handler do_sigchild;sigemptyset(newact.sa_mask);newact.sa_flags 0;//注册信号捕捉函数 SIGCHLDsigaction(SIGCHLD, newact, NULL);//1. Socket(); 创建 监听套接字 lfdlistenfd 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);//2. Bind() 绑定地址结构 Strcut scokaddr_in addr;Bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr));//3. Listen();Listen(listenfd, 20);printf(Accepting connections ...\n);while (1) {cliaddr_len sizeof(cliaddr);//accept() 阻塞监听客户端连接,接收客户端连接请求connfd Accept(listenfd, (struct sockaddr *)cliaddr, cliaddr_len);printf(-------------------------%d\n, connfd);pid fork(); //4.子进程 read(connfd) --- 小-》大 --- write(connfd)if (pid 0) {//关闭用于建立连接的套接字 lfdClose(listenfd);while (1) {//read()n Read(connfd, buf, MAXLINE);if (n 0) {printf(the other side has been closed.\n);break;}printf(received from %s at PORT %d\n,inet_ntop(AF_INET, cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));//小--大for (i 0; i n; i)buf[i] toupper(buf[i]);//write()Write(STDOUT_FILENO, buf, n);Write(connfd, buf, n);}Close(connfd);return 0;} else if (pid 0) {Close(connfd); //关闭用于与客户端通信的套接字 connfd} elseperr_exit(fork);}return 0;
} client.c客户端 /* client.c */
#include stdio.h
#include string.h
#include unistd.h
#include netinet/in.h
#include arpa/inet.h#include wrap.h#define MAXLINE 8192
#define SERV_PORT 8000int main(int argc, char *argv[])
{struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, n;sockfd Socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr);servaddr.sin_port htons(SERV_PORT);Connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr));while (fgets(buf, MAXLINE, stdin) ! NULL) {Write(sockfd, buf, strlen(buf));n Read(sockfd, buf, MAXLINE);if (n 0) {printf(the other side has been closed.\n);break;}elseWrite(STDOUT_FILENO, buf, n);}Close(sockfd);return 0;
} 三多线程thread并发服务器
server.c服务器 实现思路 在使用线程模型开发服务器时需考虑以下问题1.调整进程内最大文件描述符上限2.线程如有共享数据考虑线程同步3.服务于客户端线程退出时退出处理。退出值分离态4.系统负载随着链接客户端增加导致其它线程不能及时得到CPU 1. Socket(); 创建 监听套接字 lfd2. Bind() 绑定地址结构 Strcut scokaddr_in addr;3. Listen(); 4. while (1) { cfd Accept(lfd, );pthread_create(tid, NULL, tfn, (void *)cfd);pthread_detach(tid); // pthead_join(tid, void **); 新线程---专用于回收子线程}5. 子线程void *tfn(void *arg) {// close(lfd) 不能关闭。 主线程要使用lfdread(cfd)小--大write(cfd)pthread_exit(void *)10; } 代码逻辑 #include stdio.h
#include string.h
#include arpa/inet.h
#include pthread.h
#include ctype.h
#include unistd.h
#include fcntl.h#include wrap.h#define MAXLINE 8192
#define SERV_PORT 8000//定义一个结构体, 将地址结构跟cfd捆绑 struct s_info { struct sockaddr_in cliaddr;int connfd;};//子线程void *do_work(void *arg){int n,i;struct s_info *ts (struct s_info*)arg;char buf[MAXLINE];char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 可用[d查看while (1) { //读客户端//read(cfd)n Read(ts-connfd, buf, MAXLINE); //跳出循环,关闭cfdif (n 0) {printf(the client %d closed...\n, ts-connfd);break; }//打印客户端信息(IP/PORT)printf(received from %s at PORT %d\n,inet_ntop(AF_INET, (*ts).cliaddr.sin_addr, str, sizeof(str)),ntohs((*ts).cliaddr.sin_port)); //小写--大写for (i 0; i n; i) buf[i] toupper(buf[i]); //写出至屏幕Write(STDOUT_FILENO, buf, n); //回写给客户端Write(ts-connfd, buf, n); }Close(ts-connfd);return (void *)0;
}int main(void)
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;pthread_t tid;struct s_info ts[256]; //创建结构体数组.int i 0;//1. Socket(); 创建 监听套接字 lfdlistenfd Socket(AF_INET, SOCK_STREAM, 0); //创建一个socket, 得到lfdbzero(servaddr, sizeof(servaddr)); //地址结构清零servaddr.sin_family AF_INET;servaddr.sin_addr.s_addr htonl(INADDR_ANY); //指定本地任意IPservaddr.sin_port htons(SERV_PORT); //指定端口号 //2. Bind() 绑定地址结构 Strcut scokaddr_in addr;Bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr)); //绑定//3. Listen();Listen(listenfd, 128); //设置同一时刻链接服务器上限数printf(Accepting client connect ...\n);while (1) {cliaddr_len sizeof(cliaddr);//accept() 阻塞监听客户端连接connfd Accept(listenfd, (struct sockaddr *)cliaddr, cliaddr_len); //阻塞监听客户端链接请求ts[i].cliaddr cliaddr;ts[i].connfd connfd;pthread_create(tid, NULL, do_work, (void*)ts[i]);pthread_detach(tid); //子线程分离,防止僵线程产生.i;}return 0;
} client.c客户端 /* client.c */
#include stdio.h
#include string.h
#include unistd.h
#include netinet/in.h
#include arpa/inet.h
#include wrap.h#define MAXLINE 80
#define SERV_PORT 8000int main(int argc, char *argv[])
{struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, n;sockfd Socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr.s_addr);servaddr.sin_port htons(SERV_PORT);Connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr));while (fgets(buf, MAXLINE, stdin) ! NULL) {Write(sockfd, buf, strlen(buf));n Read(sockfd, buf, MAXLINE);if (n 0)printf(the other side has been closed.\n);elseWrite(STDOUT_FILENO, buf, n);}Close(sockfd);return 0;
}