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,感觉还行。公司计算机不能接外网,所以我发个博客,用手机看(个人电脑不推荐带),到时候看一些函数作用啥的看自己的代码好想起。

相关推荐
sinat_3842410925 分钟前
使用 npm 安装 Electron 作为开发依赖
服务器
朝九晚五ฺ40 分钟前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
自由的dream44 分钟前
Linux的桌面
linux
xiaozhiwise1 小时前
Makefile 之 自动化变量
linux
Kkooe2 小时前
GitLab|数据迁移
运维·服务器·git
意疏3 小时前
【Linux 篇】Docker 的容器之海与镜像之岛:于 Linux 系统内探索容器化的奇妙航行
linux·docker
虚拟网络工程师4 小时前
【网络系统管理】Centos7——配置主从mariadb服务器案例(下半部分)
运维·服务器·网络·数据库·mariadb
BLEACH-heiqiyihu4 小时前
RedHat7—Linux中kickstart自动安装脚本制作
linux·运维·服务器
一只爱撸猫的程序猿4 小时前
一个简单的Linux 服务器性能优化案例
linux·mysql·nginx