1.点对点聊天主要实现一个客户端与一个服务器端的通讯模型。
2.点对点聊天的实现需要服务器和客户端都得有至少两个进程来实现,一个用来发送给对方,一个用来接收对方发送的消息。
3.设计阶段需要考虑当进程结束了,或者接收到来自对方的FIN,应不应该通知其他进程,当进程结束了不要忘了break 退出循环。
代码: server.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<stdlib.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main(void){
int connfd,listenfd;
struct sockaddr_in cliaddr,servaddr;
if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0){ //定义套接字
ERR_EXIT("socket");
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(45678); //设置套接字属性
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0){ //绑定
ERR_EXIT("bind");
}
if(listen(listenfd,SOMAXCONN )< 0 ) //监听
ERR_EXIT("listen");
socklen_t len = sizeof(cliaddr);
if((connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&len))< 0 ) //accept(),注意len 是一个值-结果类型的变量,进去的值和出来的值都有用
ERR_EXIT("accept"); //很多人在写上行if语句的时候都不把赋值语句用小括号括起来,导致accept失败,其中原因是c语言功底不足
pid_t pid;
pid = fork();
if(pid == 0){ //如果是子进程
char sendbuf[1024] = {0};
while(fgets(sendbuf,sizeof(sendbuf),stdin ) != NULL){
write(connfd,sendbuf,sizeof(sendbuf));
memset(sendbuf,0,sizeof(sendbuf));
}
exit(EXIT_SUCCESS);
}else if(pid<0){
ERR_EXIT("fork");
}else{ //如果是父进程
char recvbuf[1024];
int n;
while(1){
memset(recvbuf,0,sizeof(recvbuf));
n = read(connfd,recvbuf,sizeof(recvbuf)); //从socket里面读出数据放到缓冲区里面
if(n == 0){
printf("client close");
break; //收到来自对方的FIN,一定要break,让程序退出来,否则还要读,读又读到0,导致了一个死循环。
}else if(n == -1){
ERR_EXIT("read");
}
fputs(recvbuf,stdout);
}
close(connfd);
exit(EXIT_SUCCESS);
}
return 0;
}
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<stdlib.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main(void){
int connfd;
struct sockaddr_in servaddr;
if((connfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0){
ERR_EXIT("socket");
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = htons(45678);
if(connect(connfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0){ //直接connect;
ERR_EXIT("connect");
}
pid_t pid;
pid = fork();
if(pid == -1){
ERR_EXIT("fork");
}else if(pid == 0){
char recvbuf[1024];
while(1){
memset(&recvbuf,0,sizeof(recvbuf));
int n = read(connfd,recvbuf,sizeof(recvbuf));
if(n == -1){
ERR_EXIT("read");
break;
}else if(n == 0){ //子进程用来接收,当接收到来自对方的FIN 时,跳出循环。
printf("peer close!\n");
break;
}
fputs(recvbuf,stdout); //将收到的消息打印到屏幕上
}
}else if(pid > 0){
char sendbuf[1024] = {0};
while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL){ //父进程用来发送消息,如果不是 Ctrl + C 就执行写操作。
write(connfd,sendbuf,strlen(sendbuf));
memset(sendbuf,0,sizeof(sendbuf));
}
}
return 0;
}