采用TCP协议的CS架构:
服务器使用了多进程,创建了三个进程:父进程用来检测客户端连接情况,子进程用来接收客户端消息,孙进程用来向连接上来的客户端发送“hello”。
为优化代码结构,服务器中的接收消息和发送消息都是使用了execl函数封装。
客户端使用了多线程,主线程用来向服务器发送消息,次线程用来接收服务器的问候。
遇到的问题:客户端强制退出时,服务器端将出现发送异常抛出SIGPIPE;
解决方法:在发送函数里使用signal信号函数将SIGPIPE信号忽略,并且结束进程。
备注:只是简单学习并发通信,并没有对代码进行细致优化,存在一些bug。
服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <error.h>
#define PORT 8888
int main()
{
int sockfd;
int cfd;
int ret;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int clen;
char buf[1024];
pid_t pid;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("socket");
exit(1);
}
printf("socket success!\n");
int opt = 1;
setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr("192.168.1.16");
ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //(struct sockaddr *)&
if (ret == -1)
{
perror("bind");
exit(1);
}
printf("bind success!\n");
ret = listen(sockfd, 5);
if (ret == -1)
{
perror("listen");
exit(1);
}
printf("listen success!\n");
clen = sizeof(client_addr);
while (1)
{
bzero(&client_addr, sizeof(client_addr));
cfd = accept(sockfd, (struct sockaddr *)&client_addr, &clen);
if (cfd == -1)
{
perror("accept");
exit(1);
}
printf("accept success!\n");
printf("client port = %d,client ip = %s\n",ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));
//要有三个进程, 父进程等待客户端连接,子进程接收客户端消息,孙进程持续向客户端发送字符串
pid = fork();
if (pid < 0)
{
perror("fork");
exit(1);
}
else if (pid > 0)
{
close(cfd); //子进程已经获得cfd,父进程继续等待连接
}
else
{
char cfd_buf[1024]; //存放重定向的cfd
memset(cfd_buf, 0, sizeof(cfd_buf));
sprintf(cfd_buf, "%d", cfd); //将cfd按照整形转成字符串重定向到cfd_buf里
//之后要用atoi()将字符串转成整数
pid_t pid2 = fork();
if (pid2 < 0)
{
perror("fork2");
exit(1);
}
else if (pid2 > 0)
{
execl("./recv_msg", "./recv_msg", cfd_buf, NULL);
}
else
{
execl("./send_msg", "./send_msg", cfd_buf, NULL);
}
}
}
close(sockfd);
return 0;
}
服务器发送消息send_msg():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ret;
int cfd = atoi(argv[1]); //将字符串转成整数,
while (1)
{
ret = send(cfd, "hello", strlen("hello"), 0);
signal(SIGPIPE, SIG_IGN);
if (ret < 0)
{
perror("send");
_exit(1);
}
else if (ret == 0)
{
printf("%d is exit!\n", cfd);
_exit(1);
}
else
{
sleep(2);
}
}
}
服务器接收消息recv_msg():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char*argv[])
{
int ret;
char buf[1024];
int cfd = atoi(argv[1]);
while (1)
{
memset(buf, 0, sizeof(buf));
ret = recv(cfd, buf, sizeof(buf), 0); //0阻塞接收
if (ret < 0)
{
perror("recv");
exit(1);
}
else if (ret == 0)
{
printf("%d is exit!(child)\n", cfd);
shutdown(cfd, SHUT_RDWR);
_exit(1);
}
else
{
printf("recv : %s\n", buf);
}
}
return 0;
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#define PORT 8888
void *recv_msg(void *arg)
{
int ret;
char buf[1024];
int cfd = *((int *)arg);
while (1)
{
memset(buf, 0, sizeof(buf));
ret = recv(cfd, buf, sizeof(buf), 0);
if (ret < 0)
{
perror("recv");
exit(1);
}
else if (ret == 0)
{
printf("%d is exit\n", cfd);
pthread_exit(NULL);
}
else
{
printf("client recv: %s\n", buf);
}
}
pthread_exit(NULL);
}
int main()
{
int sockfd;
int ret;
char buf[1024];
pthread_t thread;
struct sockaddr_in server_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("socket");
exit(1);
}
printf("socket success!\n");
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr("192.168.1.16");
ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1)
{
perror("connect");
exit(1);
}
printf("connect success!\n");
if (pthread_create(&thread, NULL, recv_msg, (void *)&sockfd) != 0)
{
perror("pthread create");
exit(1);
}
while (1)
{
memset(buf, 0, sizeof(buf));
printf("please input send message:\n");
gets(buf);
send(sockfd, buf, strlen(buf), 0);
}
shutdown(sockfd, SHUT_RDWR);
return 0;
}