前言
之前找工作的之后写了一些网络编程的笔记和代码,然后现在放到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,感觉还行。公司计算机不能接外网,所以我发个博客,用手机看(个人电脑不推荐带),到时候看一些函数作用啥的看自己的代码好想起。