linux网络编程实战

news/2024/9/29 2:44:21 标签: linux, 服务器

前言

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

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

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

makefile文件

        这个makefile文件都是通用的。客户端自行将server改为client就可以。-pthread在不用多线程的时候可以删掉。

CXX = gcc
OBJS = server.o wrap.o 
TARGET = server
$(TARGET):$(OBJS)
	$(CXX) $^ -o $@ -pthread
$*.o:$%.c
	$(CXX) -c $< -o $@
.PHONY: clean
clean:
	rm -rf $(OBJS) $(TARGET) 

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

#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:

#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

#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;
}

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

        服务器的代码文件名称均为server.c

#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创建子进程处理客户端请求)

#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;//创建套接字
	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文件中加上即可)

#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用来监听客户端请求))

#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是半成品,了解即可)

#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转接)

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


http://www.niftyadmin.cn/n/5682280.html

相关文章

linux从入门到精通--从基础学起,逐步提升,探索linux奥秘(六)

linux从入门到精通–从基础学起&#xff0c;逐步提升&#xff0c;探索linux奥秘&#xff08;六&#xff09; 一、linux高级指令&#xff08;1&#xff09; 1、hostname指令 1&#xff09;作用&#xff1a;操作服务器的主机名&#xff08;读取、设置&#xff09; 2&#xff0…

使用C#,MSSQL开发的钢结构加工系统

很久以前的项目&#xff0c;上位机使用C#开发。数据库使用mssql。控制系统选用了三菱PLC&#xff0c;上位机和PLC之间走ModbusTCP通讯协议。 主要功能&#xff1a;读取加工文件&#xff08;csv格式&#xff09;&#xff0c;导入到数据库&#xff0c;并根据机床刀具规则&#x…

青动CRM V3.2.1

全面解决企业销售团队的全流程客户服务难题旨在助力企业销售全流程精细化、数字化管理&#xff0c;全面解决企业销售团队的全流程客户服务难题&#xff0c;帮助企业有效盘活客户资源、量化销售行为&#xff0c;合理配置资源、建立科学销售体系&#xff0c;提升销售业绩。标准授…

2024.9.26 Spark学习

资料&#xff1a; Spark基础入门-第一章-1.1-Spark简单介绍_哔哩哔哩_bilibili &#xff08;1&#xff09;基础知识 Apache Spark 是用于大规模数据&#xff08;large-scale data&#xff09;处理的统一分析引擎。 分布式处理数据 PySpark模块 Spark 和 Hadoop 有区别&…

长列表加载性能优化

一、长列表优化概述 列表是应用开发中最常见的一类开发场景&#xff0c;它可以将杂乱的信息整理成有规律、易于理解和操作的形式&#xff0c;便于用户查找和获取所需要的信息。应用程序中常见的列表场景有新闻列表、购物车列表、各类排行榜等。随着信息数据的累积&#xff0c;特…

JavaScript 操作 DOM元素CSS 样式的几种方法

JavaScript 操作 DOM元素CSS 样式的几种方法 直接通过元素的 style 属性来设置内联样式。 // 获取元素 const element document.getElementById(myElement);// 设置单个样式属性 element.style.color red; element.style.fontSize 20px;// 设置多个样式属性 element.style…

Time-MoE : 时间序列领域的亿级规模混合专家基础模型

Time-MoE : 时间序列领域的亿级规模混合专家基础模型 时间序列预测一直是量化研究和工业应用中的重要课题。随着深度学习技术的发展&#xff0c;大规模预训练模型在自然语言处理和计算机视觉领域取得了显著进展&#xff0c;但在时间序列预测领域&#xff0c;这些模型的规模和运…

K8S的Pod IP

pod 的ip 一般是提供给pod1与pod2之间的通信&#xff0c;它有两个特点 1. Pod IP会随着Pod实例 的创新创建&#xff08;重启&#xff09;发生变化&#xff1b; 2. Pod IP只在集群内节点可见&#xff0c;外部无法直接访问