1、 REUSEADDR
(1) 为了解决TIME_WAIT状态下不能马上重启程序的问题,所以服务器要设置REUSEADDA属性。使用setsockopt进行设置
(2) setsockopt原型:
int setsockopt(int sockfd, int level, int optname,void optval, socklen_t optlen);
参数不介绍就这样设置:
int on = 1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void)&on,sizeof(on)) != 0)
{
perror(“setsockopt”);
exit(EXIT_FAILURE);
}
2、 处理多客户端连接
(1) 在之前的代码中当一个客户端去连接服务器之后其他客户端就没有办法连接服务器进行通信了。对这样的情况需要服务器端使用多进程进行解决,当有一个进程过来就开一个进程与客户端进行沟通。
(2) 实际代码:
#include <stdio.h>
#include <sys/types.h> / See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
void do_echo(int clientfd)
{
char rbuf[1024] = {0};
while(1)
{
memset(rbuf,0,sizeof(rbuf));
int iread_ret = read(clientfd,rbuf,sizeof(rbuf));
if(iread_ret == 0)
{
printf("client is close\n");
break;
}
if(iread_ret == -1)
{
handle_error("read");
}
write(clientfd,rbuf,strlen(rbuf));
printf("%s\n",rbuf);
}
}
int main()
{
int sockfd = -1,clientfd = -1;
struct sockaddr_in addr, clientAddr;
int acceptLen = 0;
if((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1)
{
printf("socket\n");
exit(-1);
}
/*
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
*/
/*
struct sockaddr_in
{
sa_family_t sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}
*/
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(5883);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int on = 1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on)) != 0)\
{
handle_error("setsockopt");
}
if(bind(sockfd, (const struct sockaddr*)&addr,sizeof(addr)) == -1)
{
handle_error("bind");
}
/*
int listen(int sockfd, int backlog);
*/
if(listen(sockfd,SOMAXCONN) == -1)
{
printf("listen\n");
exit(-1);
}
/*int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);*/
memset(&clientAddr,0,sizeof(&clientAddr));
acceptLen = sizeof(clientAddr);
while(1)
{
if((clientfd = accept(sockfd,(struct sockaddr*)&clientAddr,&acceptLen)) == -1) //在这里阻塞,有则连接,没有的话继续阻塞。
{
handle_error("listen\n");
}
//如果accept这位老哥返回东西了则开启进程
pid_t tpid = fork();
if(tpid == 0)
{
close(sockfd);//这个子进程只需要读东西,不要sockfd
do_echo(clientfd);
exit(EXIT_SUCCESS);
}
close(clientfd);//父进程只需要监听,不要读东西
}
close(sockfd);
close(clientfd);
return 0;
}
3、 点对点聊天程序的实现
(1) 实现一个客户端和一个服务器看,开一个进程,子进程进行读(或者写),父进程进行写(或者读)。客户端和服务器都这么干
(2) 需要注意的是当客户端或者服务器端关掉之后,要注意僵尸进程的出现。使用信号解决,当一个进程关闭之后提醒另外一个进程也关闭。
(3)服务端代码:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <signal.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
void handler(int sig)
{
printf("close lalala %d\n",sig);
exit(EXIT_SUCCESS);
}
int main()
{
int sockfd = -1,clientfd = -1;
struct sockaddr_in addr, clientAddr;
int acceptLen = 0;
if((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1)
{
printf("socket\n");
exit(-1);
}
/*
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
*/
/*
struct sockaddr_in
{
sa_family_t sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}
*/
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(5883);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int on = 1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on)) != 0)\
{
handle_error("setsockopt");
}
if(bind(sockfd, (const struct sockaddr*)&addr,sizeof(addr)) == -1)
{
handle_error("bind");
}
/*
int listen(int sockfd, int backlog);
*/
if(listen(sockfd,SOMAXCONN) == -1)
{
printf("listen\n");
exit(-1);
}
/*int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);*/
memset(&clientAddr,0,sizeof(&clientAddr));
acceptLen = sizeof(clientAddr);
if((clientfd = accept(sockfd,(struct sockaddr*)&clientAddr,&acceptLen)) == -1)
{
printf("listen\n");
exit(-1);
}
pid_t tpid = fork();
if(tpid == 0) //子进程发数据
{
char wbuf[1024] = {0};
while(fgets(wbuf,sizeof(wbuf),stdin) != NULL )
{
/*char *fgets(char *s, int size, FILE *stream);*/
//fputs(wbuf,stdout);
write(clientfd,wbuf,strlen(wbuf));
memset(wbuf,0,sizeof(wbuf));
}
kill(tpid,SIGUSR1);
exit(EXIT_SUCCESS);
}
else //父进程读数据
{
signal(SIGUSR1,handler);
char rbuf[128] = {0};
while(1)
{
memset(rbuf,0,sizeof(rbuf));
int readRet = read(clientfd,rbuf,sizeof(rbuf));
if(readRet == 0)
{
printf("peer close");
break;
}
else if(readRet == -1)
{
close(clientfd);
handle_error("read");
}
printf("%s",rbuf);
}
exit(EXIT_SUCCESS);
}
return 0;
}
(4)客户端代码:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <signal.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
void handler(int sig)
{
printf("i am close,sig = %d\n", sig);
exit(EXIT_SUCCESS);
}
int main()
{
int sockfd = -1;
struct sockaddr_in addr;
int acceptLen = 0;
char tbuf[128] = {0};
char rbuf[128] = {0};
if((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1)
{
printf("socket\n");
exit(-1);
}
/*
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
*/
/*
struct sockaddr_in
{
sa_family_t sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}
*/
memset(&addr,0,sizeof(&addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(5883);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(sockfd, (const struct sockaddr*)&addr,sizeof(addr)) == -1)
{
handle_error("connect");
}
pid_t tpid = fork();
if(tpid == 0)
{
char rbuf[1024] = {0};
int readRet = -1;
while(1)
{
readRet = read(sockfd,rbuf,sizeof(rbuf));
if(readRet == 0)
{
printf("server close\n");
break;
}
else if(readRet == -1)
{
close(sockfd);
handle_error("read");
}
printf("%s",rbuf);
memset(rbuf,0,sizeof(rbuf));
}
printf("close\n");
close(sockfd);
kill(tpid,SIGUSR1);
exit(EXIT_SUCCESS);
}
else
{
signal(SIGUSR1,handler);
char wbuf[1024] = {0};
while(fgets(wbuf,sizeof(wbuf),stdin) != 0)
{
write(sockfd,wbuf,strlen(wbuf));
memset(wbuf,0,sizeof(wbuf));
}
close(sockfd);
exit(EXIT_SUCCESS);
}
return 0;
}
本文介绍了网络编程中如何处理多客户端连接的问题。通过设置SO_REUSEADDR选项解决TIME_WAIT状态重启问题,采用多进程方式确保每个客户端都能与服务器通信。同时,文中还提到了点对点聊天程序的实现,包括父进程与子进程的角色分配,以及通过信号处理僵尸进程的潜在问题。给出了服务端和客户端的代码示例。

2503

被折叠的 条评论
为什么被折叠?



