套接字编程:基于TCP协议的通信编程(优化)
问题解决
- 解决了上篇代码中每个客户端只能和服务器通信一次的问题
解决措施
- 通过fork()创建子进程来解决这个问题,每当服务器与一个客户端新建一个连接后,这时就为其创建一个子进程。
- 然后让子进程再来处理用新套接字newfd进行数据收发的工作,然后在处理完这个客户端与服务器的通信任务后,再自动退出。
- 而父进程则只负责用最开始生成的套接字sockfd来监听,并为新的连接生成套接字newfd。
- 最后每当有一个子进程退出的时候用通过回调函数signal(SIGCHLD,sigcb),处理掉系统中的僵尸子进程
tcp_process.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<netinet/in.h>//地址结构
#include<arpa/inet.h>//字节序转换接口
#include<sys/socket.h>//套接字接口
#include<sys/wait.h>
#define MAX_LISTEN_NUM 5
void sigcb(int no)//处理掉僵尸进程
{
while(waitpid(-1,NULL,WNOHANG)>0);
}
int main()
{
signal(SIGCHLD,sigcb);
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0){
perror("socket error");
return -1;
}
//2.绑定地址信息
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(9000);
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
socklen_t len=sizeof(addr);
int ret = bind(sockfd,(struct sockaddr*)&addr,len);
if(ret<0){
perror("bind error");
return -1;
}
//3.开始监听
ret =listen(sockfd,MAX_LISTEN_NUM);
if(ret<0){
perror("listen error");
return -1;
}
//4.获取新连接
while(1){
struct sockaddr_in cliaddr;
int newfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len);
if(newfd<0){
perror("accept error");
continue;
}
if(fork()==0){//表示子进程
while(1){
//5.使用新连接与客户端通信(收发数据)
char buf[1024]={0};
//接收数据
ret=recv(newfd,buf,1023,0);
if(ret<0){
perror("recv error");
close(newfd);
return -1;
}else if(ret==0){//返回值为0,表示该客户端已断开连接
printf("peer shutdown\n");
close(newfd);
return -1;
}
printf("%s:%d say:%s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buf);
//发送数据
memset(buf,0x00,1024);
printf("server say:");
fflush(stdout);
scanf("%s",buf);
ret=send(newfd,buf,strlen(buf),0);
}
exit(0);
}
close(newfd);
}
//6.关闭套接字
close(sockfd);
return 0;
}
tcp_client.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>//地址结构
#include<arpa/inet.h>//字节序转换接口
#include<sys/socket.h>//套接字接口
int main()
{
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0){
perror("socket error");
return -1;
}
//2.绑定地址信息(不推荐)
//3.向服务端发起连接请求
struct sockaddr_in srv_addr;
srv_addr.sin_family=AF_INET;
srv_addr.sin_port=htons(9000);
srv_addr.sin_addr.s_addr=inet_addr("192.168.204.144");
socklen_t len=sizeof(srv_addr);
int ret=connect(sockfd,(struct sockaddr*)&srv_addr,len);
if(ret<0){
perror("connect error");
return -1;
}
//4.收发数据
while(1){
//发送数据
printf("client say:");
fflush(stdout);
char buf[1024]={0};
scanf("%s",buf);
ret=send(sockfd,buf,strlen(buf),0);
if(ret<0){
perror("send error");
return -1;
}
//接收数据
memset(buf,0x00,1024);
ret=recv(sockfd,buf,1023,0);
if(ret<0){
perror("recv errror");
return -1;
}else if (ret==0){
printf("peer shutdown");
return -1;
}
printf("server say:%s\n",buf);
}
//5.关闭套接字
close(sockfd);
return 0;
}
通信演示