linux网络编程实战

前言

之前找工作的之后写了一些网络编程的笔记和代码,然后现在放到csdn上保存一下。有几个版本的,看看就好。就是简单的实现一下服务端和客户端之间的交互的,还没有我之前上linux编程课写的代码复杂。

哦对了,这个网络编程的代码对错误处理函数进行了一些封装,就是将select、accept等函数进一步封装了一下,问题不大。要是有朋友看到这篇博客想学习啥的,有问题可以问我。这个其实是在b站上看视频写的代码,嗯,其实没必要看我的代码的。

最后,客户端的代码都是一样的,不用客户端的代码其实也可以,启动服务端后直接用nc命令进行测试就行。

使用环境

makefile文件

直接将所有文件复制到同一个目录下,然后打开终端使用make命令就全部编译好了。

cpp 复制代码
# 定义编译器
CXX=gcc

# 定义编译选项,-Wall表示将所有警告打开,这里注释掉
# CFLAGS=-Wall

# 定义多个目标文件
TARGET=client
TARGET1=server1
TARGET2=server2
TARGET3=server3
TARGET4=server4
TARGET5=server5
TARGET6=server6

# 定义多个源文件
SRC=client.c wrap.c
SRC1=server1.c wrap.c
SRC2=server2.c wrap.c
SRC3=server3.c wrap.c
SRC4=server4.c wrap.c
SRC5=server5.c wrap.c
SRC6=server6.c wrap.c

# 默认目标
all: $(TARGET) $(TARGET1) $(TARGET2) $(TARGET3) $(TARGET4) $(TARGET5) $(TARGET6)

# 编译第一个文件
$(TARGET): $(SRC)
	$(CXX) -o $(TARGET) $(SRC)
# 编译其他文件
$(TARGET1): $(SRC1)
	$(CXX) -o $(TARGET1) $(SRC1)
$(TARGET2): $(SRC2)
	$(CXX) -o $(TARGET2) $(SRC2)
$(TARGET3): $(SRC3)
	$(CXX) -o $(TARGET3) $(SRC3) -pthread
$(TARGET4): $(SRC4)
	$(CXX) -o $(TARGET4) $(SRC4)
$(TARGET5): $(SRC5)
	$(CXX) -o $(TARGET5) $(SRC5)
$(TARGET6): $(SRC6)
	$(CXX) -o $(TARGET6) $(SRC6)

# 清理编译生成的文件
.PHONY: clean
clean:
	rm -f  $(TARGET) $(TARGET1) $(TARGET2) $(TARGET3) $(TARGET4) $(TARGET5) $(TARGET6)
	

错误处理函数封装头文件wrap.h

cpp 复制代码
#ifndef __WRAP_H_
#define __WRAP_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);
#endif

错误处理函数封装wrap.c:

cpp 复制代码
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.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;
		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;
}
ssize_t Readn(int fd, void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nread;
	char *ptr;

	ptr = vptr;
	nleft = n;

	while (nleft > 0) {
		if ( (nread = read(fd, ptr, nleft)) < 0) {
			if (errno == EINTR)
				nread = 0;
			else
				return -1;
		} else if (nread == 0)
			break;
		nleft -= nread;
		ptr += nread;
	}
	return n - nleft;
}

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;
			else
				return -1;
		}
		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}

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) {
			if (errno == EINTR)
				goto again;
			return -1;	
		} else if (read_cnt == 0)
			return 0;
		read_ptr = read_buf;
	}
	read_cnt--;
	*ptr = *read_ptr++;
	return 1;
}

ssize_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++ = c;
			if (c == '\n')
				break;
		} else if (rc == 0) {
			*ptr = 0;
			return n - 1;
		} else
			return -1;
	}
	*ptr = 0;
	return n;
}

客户端代码client.c

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "wrap.h"

#define SERVER_PORT 8888 //需要与服务器相同

int main(int argc, char *argv[])
{
	int sc=0,err=0,slen=0;
	char message[1024]={0};
	struct sockaddr_in server_addr;
	
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(SERVER_PORT);
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//或inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr.s_addr);
    
	sc=Socket(AF_INET,SOCK_STREAM,0);
	err = Connect(sc,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
	
	while(1){//持续进行通信
		slen=Read(0,message,sizeof(message));
		slen=Write(sc,message,slen);
		slen=Read(sc,message,slen);
		slen=Write(1,message,slen);
	}
	Close(sc);
	return 0;
}

第一版服务器(直接发送接受,只能一次且一个客户端)server1.c

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "wrap.h"

//宏定义
#define MAXLEN 100  //设置监听上限
#define SERVER_PORT 8888  //绑定的端口


int main(int argc, char *argv[])
{
	int ss=0,sc=0,slen=0,err=0;//创建套接字
	char message[1024]={0},client_ip[1024]={0};//创建字符串来作为缓冲区,或用BUFSIZ=4096
	struct sockaddr_in server_addr,client_addr;//创建服务器端和客户端的地址结构
	socklen_t addrlen;//地址结构的长度
	
	bzero(&server_addr,sizeof server_addr);//清零
	server_addr.sin_family = AF_INET;//协议族
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
	
	ss = Socket(AF_INET,SOCK_STREAM,0);
	int opt=1;
	setsockopt(ss,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
	err = Bind(ss,(struct sockaddr*)&server_addr,sizeof server_addr);//给socket绑定地址结构
	err = Listen(ss,MAXLEN);//设置监听上限
    
	addrlen = sizeof(struct sockaddr);
	sc=Accept(ss,(struct sockaddr*)&client_addr,&addrlen);
	
	printf("client ip:%s,port:%d\n",
	inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,sizeof(client_ip)),ntohs(client_addr.sin_port));
	//inet_ntop:将网络字节序转为本地字节序
	//ntohs:将网络端口转为本地端口
	
	while(1){
		slen = Read(sc,message,1024);//接收信息
		Write(1,message,slen);//将收到的信息输出,STDOUT_FILENO=1,STDIN_FILENO=0;
		
		for(int i=0;i<slen;i++)//进行大小写转换
			if(message[i]>='a'&&message[i]<='z')message[i]=message[i]-'a'+'A';
			//message[i]=toupper(message[i]);
			
		slen=Write(sc,message,slen);//将转换后的信息发回给客户端
		
	}
	Close(sc);//关闭文件描述符
	Close(ss);
	return 0;
}

第二版服务器(多进程多并发服务器,直接用fork创建子进程处理客户端请求)server2.c

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "wrap.h"
 
//宏定义
#define MAXLEN 100  //设置监听上限
#define SERVER_PORT 8888  //绑定的端口
 
void catch_child(int signum){
	while(waitpid(0, NULL,WNOHANG)>0);
	return ;
}
 
int main(int argc, char *argv[])
{
	int lfd=0,cfd=0,slen=0,ret;//创建套接字
	pid_t pid;//创建子进程
	char message[1024]={0},client_ip[1024]={0};//创建字符串来作为缓冲区,或用BUFSIZ=4096
	struct sockaddr_in server_addr,client_addr;//创建服务器端和客户端的地址结构
	socklen_t addrlen;//地址结构的长度
	
	bzero(&server_addr,sizeof server_addr);//清零
	server_addr.sin_family = AF_INET;//协议族
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
	
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
	lfd = Socket(AF_INET,SOCK_STREAM,0);
	Bind(lfd,(struct sockaddr*)&server_addr,sizeof server_addr);//给socket绑定地址结构
	Listen(lfd,MAXLEN);//设置监听上限
    
        addrlen = sizeof(struct sockaddr);
        printf("Accepting connections ...\n");
        while(1){
        	cfd = Accept(lfd,(struct sockaddr*)&client_addr,&addrlen);
        
        	printf("client ip:%s,port:%d 进入\n",
        	inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,sizeof(client_ip)),ntohs(client_addr.sin_port));
		//inet_ntop:将网络字节序转为本地字节序
		//ntohs:将网络端口转为本地端口
	
		pid=fork();
        	if(pid==0){
        		Close(lfd);
        		while(1){
				slen = Read(cfd,message,1024);//接收信息
             	 		if(slen==0){
                 			Close(cfd);
                 			exit(1);
             	 		}
				Write(1,message,slen);//将收到的信息输出,STDOUT_FILENO=1,STDIN_FILENO=0;
			
				for(int i=0;i<slen;i++)//进行大小写转换
				if(message[i]>='a'&&message[i]<='z')message[i]=message[i]-'a'+'A';
				//message[i]=toupper(message[i]);
				slen=Write(cfd,message,slen);//将转换后的信息发回给客户端
                	}
                	Close(cfd);
        		break;
        	}
        	else if(pid < 0) perr_exit("fork error");
        	else{
         		struct sigaction act;//-----还不知道这是在干嘛
             		act.sa_handler=catch_child;
             		sigemptyset(&act.sa_mask);
             		act.sa_flags = 0;
             		ret = sigaction(SIGCHLD,&act,NULL);
             		if(ret!=0) perr_exit("sigaction error");
             		Close(cfd);
             		continue;	
		}
	}
	Close(lfd);//关闭文件描述符
	return 0;
}

第三版服务器(多线程多并发服务器,创建子线程处理客户端请求,多线程在编译时要加-pthread,在makefile文件中加上即可)server3.c

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <pthread.h>
#include "wrap.h"

#define MAXLISTEN 100  //设置监听上限
#define MAXLINE 80  //缓冲区大小
#define SERVER_PORT 8888  //绑定的端口
#define MAXCLIENT 256 //最大客户端数量

struct client_info{
	struct sockaddr_in client_addr;
     	int connfd;
};
void *do_work(void *arg){
     	int slen;
	struct client_info *temp = (struct client_info*)arg;
     	char buf[MAXLINE],client_ip[1024];
     	char str[INET_ADDRSTRLEN];
     	pthread_detach(pthread_self());
     	while(1){
   		slen=Read(temp->connfd, buf, MAXLINE);
        	if(slen == 0){
             		printf("client ip:%s quit\n",
			inet_ntop(AF_INET,&(*temp).client_addr.sin_addr,client_ip,sizeof(client_ip)));
             		break;
         	}
         	Write(1,buf,slen);//输出到本地
         	printf(" received ip:%s at port:%d :",
        	inet_ntop(AF_INET,&(*temp).client_addr.sin_addr,client_ip,sizeof(client_ip)),ntohs((*temp).client_addr.sin_port));
         	//进行处理
         	for(int i=0;i<slen;i++)
             		if(buf[i]>='a'&&buf[i]<='z')buf[i]=buf[i]-'a'+'A';
             		
         	Write(temp->connfd,buf,slen);//将改好的信息发回客户端
     	}
     	Close(temp->connfd);
     	return (void*)0;
}
int main(int argc, char *argv[])
{
	int lfd=0,cfd=0;//创建套接字
    	int i=0;//i为线程编号
	struct sockaddr_in server_addr,client_addr;//创建服务器端的地址结构
	socklen_t addrlen;//地址结构的长度
	char client_ip[1024];
    	pthread_t tid;
    	struct client_info client[MAXCLIENT];//创建客户端地址结构
	
	bzero(&server_addr,sizeof server_addr);//清零
	server_addr.sin_family = AF_INET;//协议族
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
	
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
	lfd = Socket(AF_INET,SOCK_STREAM,0);
	Bind(lfd,(struct sockaddr*)&server_addr,sizeof server_addr);//给socket绑定地址结构
	Listen(lfd,MAXLISTEN);//设置监听上限
    
     	addrlen = sizeof(struct sockaddr);
     	printf("Accepting connections ...\n");
     	while(1){
        	cfd = Accept(lfd,(struct sockaddr*)&client_addr,&addrlen);
        	printf("client ip:%s,port:%d 进入\n",
        	inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,sizeof(client_ip)),ntohs(client_addr.sin_port));
        	
         	client[i].client_addr=client_addr;
         	client[i].connfd=cfd;
		/*达到线程最大数时,pthread_create出错处理,增加服务器稳定性*/
         	pthread_create(&tid, NULL, do_work, (void*)&client[i]);
         	i++;
	}
    	Close(lfd);
	return 0;
}

第四版服务器(使用select多路IO转接(借助内核,select用来监听客户端请求))server4.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "wrap.h"

#define MAXLISTEN 100  //设置监听上限
#define MAXLINE 80  //缓冲区大小
#define SERVER_PORT 8888  //设置服务端的端口

int main(int argc, char *argv[])
{
	int lfd,cfd,sockfd,slen,ret,i;//创建套接字
	int maxi;//用来保存最大的文件描述符
	int maxfd,client[FD_SETSIZE];//最大文件描述符、客户端文件描述符集合,FD_SETSIZE默认为1024
	pid_t pid;//创建子进程
	char buf[MAXLINE],str[INET_ADDRSTRLEN];//创建字符串来作为缓冲区,或用BUFSIZ=4096,str用来使用inet_ntop函数
	struct sockaddr_in server_addr,client_addr;//创建服务器端和客户端的地址结构
	socklen_t addrlen;//地址结构的长度
	fd_set rset,allset;//文件描述符集合,创建监听集合
	
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
	
	lfd = Socket(AF_INET,SOCK_STREAM,0);
	bzero(&server_addr,sizeof server_addr);//清零
	server_addr.sin_family = AF_INET;//协议族
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
	Bind(lfd,(struct sockaddr*)&server_addr,sizeof server_addr);//给socket绑定地址结构
	Listen(lfd,MAXLISTEN);//设置监听上限
	
	maxfd = lfd;
	maxi=-1;
	for(i=0;i<FD_SETSIZE;i++)client[i]=-1;//对客户端文件描述符集合进行初始化
	FD_ZERO(&allset); //将监听集合清空
	FD_SET(lfd,&allset);//将lfd添加到读集合中。
	
	addrlen = sizeof(struct sockaddr);
	printf("Accepting connections ...\n");
	while(1){
		rset=allset;//保存监听集合
		ret = select(maxfd+1,&rset,NULL,NULL,NULL); //监听文件描述符集合对应事件
		if(ret<0)perr_exit("select error");
		if(FD_ISSET(lfd,&rset)){//1 在,0 不在
			cfd = Accept(lfd,(struct sockaddr*)&client_addr,&addrlen);//建立连接,不会阻塞
			
			printf("client ip:%s at port:%d access\n",
				inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
				ntohs(client_addr.sin_port));

			for(i=0;i<FD_SETSIZE;i++)
				if(client[i]<0){
					client[i]=cfd;
					break;
				}
			
			if (i == FD_SETSIZE) {/* 达到select能监控的文件个数上限 1024 */
				fputs("too many clients\n", stderr);
				exit(1);
			}

			FD_SET(cfd,&allset);//将新产生的fd添加到监听通信描述符集合中
			if(maxfd<cfd)maxfd=cfd;//select第一个参数需要
			if(i>maxi)maxi=i;//更新client[]数组中的最大文件描述符
			if(--ret == 0) continue;//只有lfd事件,后续的for不执行
		}
		for(i=0;i<=maxi;i++){//处理满足都事件的fd
			if((sockfd = client[i])<0) continue;//将client[i]赋值给sockfd
			if(FD_ISSET(sockfd,&rset)){//找到满足读事件的fd
				slen = Read(sockfd,buf,sizeof buf);
				if(slen == 0){//当客户端关闭连接时,服务端也关闭连接
					/*printf("client ip %s at port %d quit\n",
						inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
						ntohs(client_addr.sin_port));*/
					Close(sockfd);
					FD_CLR(sockfd,&allset);//解除select对此文件描述符的监控
					client[i]=-1;
				}
				else if(slen>0){
					/*printf("received from %s at port %d :",
						inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
						ntohs(client_addr.sin_port));*/
					Write(1,buf,slen);
					for(int j=0;j<slen;j++)//进行处理
						if(buf[j]>='a'&&buf[j]<='z')buf[j]=buf[j]-'a'+'A';
					slen = Write(sockfd,buf,slen);
				}
			if(--ret == 0)break;
			}	
		}
	}
	Close(lfd);//关闭文件描述符
	return 0;
}

第五版服务器(poll多路IO转接,poll是半成品,了解即可)server5.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include "wrap.h"

#define MAXLISTEN 100  //设置监听上限
#define MAXLINE 80  //缓冲区大小
#define SERVER_PORT 8888  //设置服务端的端口
#define OPEN_MAX 1024

int main(int argc, char *argv[])
{
	int lfd,cfd,sockfd,slen,ret,i,maxi;//创建套接字,maxi为用来保存最大的文件描述符
	char buf[MAXLINE],str[INET_ADDRSTRLEN];//创建字符串来作为缓冲区,或用BUFSIZ=4096
	//str用来使用inet_ntop函数,INET_ADDESTRLEN=16
	struct sockaddr_in server_addr,client_addr;//创建服务器端和客户端的地址结构
	struct pollfd client[OPEN_MAX];
	socklen_t addrlen;//地址结构的长度
	
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
	
	lfd = Socket(AF_INET,SOCK_STREAM,0);
	bzero(&server_addr,sizeof server_addr);//清零
	server_addr.sin_family = AF_INET;//协议族
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
	
	Bind(lfd,(struct sockaddr*)&server_addr,sizeof server_addr);//给socket绑定地址结构
	
	Listen(lfd,MAXLISTEN);//设置监听上限
	
	maxi=0;
	client[0].fd=lfd;
	client[0].events=POLLRDNORM;
	for(i=1;i<OPEN_MAX;i++)client[i].fd=-1;
	
	addrlen = sizeof(struct sockaddr);
	printf("Accepting connections ...\n");
	
	while(1){
		ret=poll(client,maxi+1,-1);
		if(client[0].revents & POLLRDNORM){//处理lfd
			cfd=Accept(lfd,(struct sockaddr*)&client_addr,&addrlen);
			printf("client ip:%s at port:%d access\n",
				inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
				ntohs(client_addr.sin_port));
				
			for(i=1;i<OPEN_MAX;i++)
				if(client[i].fd<0){
					client[i].fd=cfd;//找到client中空闲的位置并将客户端的文件描述符放入
					break;
				}
			if(i==OPEN_MAX) perr_exit("to many client");//客户端数量过多
			client[i].events=POLLRDNORM;
			
			if(i>maxi)maxi=i;
			if(--ret<=0)continue;
		}
		for(i=1;i<=maxi;i++){//处理cfd
			sockfd=client[i].fd;
			if(sockfd<0)continue;
			if(client[i].revents & (POLLRDNORM | POLLERR)){
				slen=Read(sockfd,buf,sizeof buf);
				if(slen<0){
					if(errno == ECONNRESET){//收到RST标志
						printf("client[%d](ip:%s) aborted connection\n",i,
						inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)));
						Close(sockfd);
						client[i].fd=-1;
					}else perr_exit("read error");
				}
				else if(slen == 0){//说明客户端先关闭连接
					printf("client[%d](ip:%s) closed connection\n",i,
					inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)));
					Close(sockfd);
					client[i].fd=-1;
				}
				else {
					for(int j=0;j<slen;j++)
						if(buf[j]>='a'&&buf[j]<='z')buf[j]=buf[j]-'a'+'A';
					slen=Write(sockfd,buf,slen);
				}
				if(--ret<=0)break;
			}
		}
	}
	Close(lfd);//关闭文件描述符
	return 0;
}

第六版服务器(epoll多路IO转接)server6.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include "wrap.h"

#define MAXLISTEN 100  //设置listen监听上限
#define MAXLINE 80  //缓冲区大小
#define SERVER_PORT 8888  //设置服务端的端口
#define OPEN_MAX 1024

int main(int argc, char *argv[])
{
	int lfd,cfd,sockfd,slen,ret,res,i,j,maxi,efd;//创建套接字,maxi为用来保存最大的文件描述符
	int client[OPEN_MAX];
	char buf[MAXLINE],str[INET_ADDRSTRLEN];//创建字符串来作为缓冲区,或用BUFSIZ=4096
	//str用来使用inet_ntop函数,INET_ADDESTRLEN=16
	struct sockaddr_in server_addr,client_addr;//创建服务器端和客户端的地址结构
	struct epoll_event tep,ep[OPEN_MAX];//tep为临时变量
	socklen_t addrlen;//地址结构的长度
	
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
	lfd = Socket(AF_INET,SOCK_STREAM,0);
	bzero(&server_addr,sizeof server_addr);//清零
	server_addr.sin_family = AF_INET;//协议族
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
	Bind(lfd,(struct sockaddr*)&server_addr,sizeof server_addr);//给socket绑定地址结构
	Listen(lfd,MAXLISTEN);//设置监听上限
	
	maxi = -1;
	for(i = 0;i < OPEN_MAX;i++) client[i] = -1;
	
	efd = epoll_create(OPEN_MAX);
	if(efd < 0) perr_exit("epoll_create error");
	
	tep.events = EPOLLIN;//设置临时变量
	tep.data.fd = lfd;
	
	res = epoll_ctl(efd,EPOLL_CTL_ADD,lfd,&tep);
	if(res < 0) perr_exit("epoll_ctl error");
	
	addrlen = sizeof(struct sockaddr);
	printf("Accepting connections ...\n");
	
	while(1){
		ret = epoll_wait(efd, ep, OPEN_MAX, -1);//阻塞监听,返回监听到的文件描述符数量
		if(ret < 0) perr_exit("epoll_wait error");
		
		for(i = 0;i < ret; i++){
			if(!(ep[i].events & EPOLLIN)) continue;
			
			if(ep[i].data.fd == lfd){
				cfd = Accept(lfd,(struct sockaddr *)&client_addr,&addrlen);
				printf("client ip:%s at port %d access\n",
				inet_ntop(AF_INET,&client_addr.sin_addr,str,sizeof str),
				ntohs(client_addr.sin_port));
				
				for(j=0;j<OPEN_MAX;j++)//找一个空位置存放客户端的文件描述符
					if(client[j] < 0){
						client[j]=cfd;
						break;
					}
				if(j == OPEN_MAX) perr_exit("too many clients");
				if(j > maxi) maxi=j;
				
				tep.events = EPOLLIN;
				tep.data.fd = cfd;
				res = epoll_ctl(efd,EPOLL_CTL_ADD,cfd,&tep);
				if(res < 0)perr_exit("epoll_ctl error");
			}
			else {
				sockfd = tep.data.fd;
				slen = Read(sockfd,buf,sizeof buf);
				if(slen == 0){
					for(j=0;j<=maxi;j++)
						if(client[j]==sockfd){
							client[j]=-1;
							break;
						}
					res = epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
					if(res < 0) perr_exit("eopll_ctl error");
					Close(sockfd);
					printf("client[%d] closed connection\n",j);
				}
				else{
					Write(1,buf,slen);
					for(j=0;j<slen;j++)
						if(buf[j]>='a'&&buf[j]<='z')
							buf[j]=buf[j]-'a'+'A';
					Write(sockfd,buf,slen);
				}
			}
		}
	}
	Close(lfd);//关闭文件描述符
	Close(efd);
	return 0;
}

结语

没啥好说的,找到工作之后就是当牛马了,也不知道能干几年,已经在想养老了(但是养老计算器说还要干40年,我想我真能活到那时候吗)。现在是还没转正,工作也是最近才找到的(因为考研失败摆烂了好长时间),是一家国企965,感觉还行。公司计算机不能接外网,所以我发个博客,用手机看(个人电脑不推荐带),到时候看一些函数作用啥的看自己的代码好想起。

相关推荐
大树8814 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠14 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush415 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52015 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz15 小时前
Maven依赖冲突
java·服务器·maven
不会C语言的男孩16 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈16 小时前
Unix 与 Linux 异同小叙
linux·服务器·unix
程序猿阿伟17 小时前
《Chrome离线扩展安装的底层逻辑与场景落地指南》
服务器·网络·chrome
凡人叶枫18 小时前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
AC赳赳老秦18 小时前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw