目录
端口复用
单边的close,会有一个等待的状态,这个时候有1分钟的时间,端口还开启着。这个时候再打开服务器,可能会有端口依旧绑定的情况
端口复用来重启这个端口,就能重新绑定了
cpp
int optval=1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
cpp
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
int main(){
//1.创建socket(用于监听的套接字)
int lfd=socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1){
perror("socket");
exit(0);
}
int optval=1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
//2.绑定
struct sockaddr_in saddr;
saddr.sin_family=AF_INET;
//indet_pton(AF_INET,"192.168.227.129",saddr.sin_addr.s_addr);
saddr.sin_addr.s_addr=INADDR_ANY;
saddr.sin_port=htons(9999);
int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret==-1){
perror("bind");
exit(0);
}
//3.监听
ret=listen(lfd,8);
if(ret==-1){
perror("listen");
exit(0);
}
//4.接收客户端的连接
struct sockaddr_in clientaddr;
socklen_t len=sizeof(clientaddr);
int cfd=accept(lfd,(struct sockaddr *)&clientaddr,&len);
if(cfd==-1){
perror("accept");
exit(0);
}
//输出客户端的信息
char clientIP[16];
inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
unsigned short clientPort =ntohs(clientaddr.sin_port);
printf("client ip is %s,port is %d\n",clientIP,clientPort);
//5.通信
char recvBuf[1024]={0};
//获取客户端的数据
while(1){
int num=recv(cfd,recvBuf,sizeof(recvBuf),0);
if(num==-1){
perror("recv");
exit(-1);
}else if(num>0){
printf("recv client data:%s\n",recvBuf);
}else if(num==0){
//表示客户端断开连接
printf("client closed...");
break;
}
//小写转大写
for(int i=0;i<len;++i){
recvBuf[i]=toupper(recvBuf[i]);
}
printf("after buf=%s\n",recvBuf);
//大写字符串发给客户端
ret=send(cfd,recvBuf,strlen(recvBuf)+1,0);
if(ret==-1){
perror("send");
return -1;
}
}
//关闭文件描述符
close(cfd);
close(lfd);
return 0;
}
cpp
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(){
//1.创建套接字
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){
perror("socket");
exit(-1);
}
//2.连接服务器端
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);
serveraddr.sin_port=htons(9999);
int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1){
perror("connect");
exit(-1);
}
char recvBuf[1024]={0};
//3.通信
while(1){
char sendBuf[1024]={0};
fgets(sendBuf,sizeof(sendBuf),stdin);
//给服务器发送数据
write(fd,sendBuf,strlen(sendBuf)+1);
int len=read(fd,sendBuf,sizeof(sendBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv server data:%s\n",sendBuf);
}else if(len==0){
//表示客户端断开连接
printf("server closed...");
}
}
//关闭连接
close(fd);
return 0;
}
I/O多路复用
select
构造一个关于文件描述符的列表
监听文件是否发送数据,告诉进程有哪些描述符需要进行I/O操作
两个客户端也可以
并没有用多进程或者多线程,只使用了select也能完成多客户端的连接
client.c
cpp
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(){
//1.创建套接字
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){
perror("socket");
exit(-1);
}
//2.连接服务器端
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
inet_pton(AF_INET,"127.0.0.1",&serveraddr.sin_addr.s_addr);
serveraddr.sin_port=htons(9999);
int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1){
perror("connect");
exit(-1);
}
//3.通信
int num=0;
while(1){
char sendBuf[1024]={0};
sprintf(sendBuf,"send data %d",num++);
sleep(1);
//给服务器发送数据
write(fd,sendBuf,strlen(sendBuf)+1);
int len=read(fd,sendBuf,sizeof(sendBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv server data:%s\n",sendBuf);
}else if(len==0){
//表示客户端断开连接
printf("server closed...");
}
}
//关闭连接
close(fd);
return 0;
}
select.c
cpp
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/time.h>
#include<sys/types.h>
#include<sys/select.h>
int main(){
//创建socket
int lfd=socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in saddr;
saddr.sin_port=htons(9999);
saddr.sin_family=AF_INET;
saddr.sin_addr.s_addr=INADDR_ANY;
//绑定
bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
//监听
listen(lfd,8);
//创建一个fd_set的集合,存放的是需要检测的文件描述符
fd_set rdset,tmp;
FD_ZERO(&rdset);
FD_SET(lfd,&rdset);
int maxfd=lfd;
while(1){
tmp=rdset;
//调用select系统函数,让内核帮检测哪些文件描述符有数据
int ret=select(maxfd+1,&tmp,NULL,NULL,NULL);
if(ret==-1){
perror("select");
exit(-1);
}else if(ret==0){
continue;
}else if(ret>0){
//说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
if(FD_ISSET(lfd,&tmp)){
//表示有新的客户端连接进来了
struct sockaddr_in cliaddr;
int len=sizeof(cliaddr);
int cfd=accept(lfd,(struct sockaddr *)&cliaddr,&len);
//将新的文件描述符加入到集合中
FD_SET(cfd,&rdset);
//更新最大的文件描述符
maxfd=maxfd>cfd? maxfd:cfd;
}
for(int i=lfd+1;i<=maxfd;i++){
if(FD_ISSET(i,&rdset)){
//说明这个文件描述符对应的客户端发来了数据
char buf[1024]={0};
int len=read(i,buf,sizeof(buf));
if(len==-1){
perror("read");
exit(-1);
}else if(len==0){
printf("client closed..\n");
close(i);
FD_CLR(i,&rdset);
}else if(len>0){
printf("read buf:%s\n",buf);
write(i,buf,strlen(buf)+1);
}
}
}
}
}
close(lfd);
return 0;
}