一.需求与开发环境
利用Linux系统中的socket APIs实现一对服务器与客户端程序,开发工具为VS Code或其他Linux下代码编辑器,编译器为gcc,具体要求如下:
1. 客户端
用户在终端运行客户端程序,命令格式如下:
$>./echoclient
以上命令格式中服务器IP地址不可省略(一般测试时为127.0.0.1);方括号中的重定向可以没有,当没有重定向文件时,用户键盘输入为客户端发送给服务器的数据来源,每当用户按下回车键时,此行输入会被发送至服务端,并在客户端回显(来自服务端的回应),当用户在键盘上按下Ctrl c时,客户端程序结束;当有重定向文件时,客户端将文件中数据逐行发送至服务端,发送完毕且回显完毕结束程序,全过程用户无需键盘输入。
2.服务端
服务器程序在终端中以如下格式后台运行:
$>./echoserver
主进程做好套接字准备后会阻塞在accept上等待客户端请求到达;当客户端请求到达时,服务器主进程需要创建子进程为客户端服务;结束时子进程退出,主进程通过信号处理调用waitpid回收子进程;服务器可以同时处理多个客户端的请求。
二.系统设计
1.系统分析(需要的主要系统调用)
服务器端(server):
Socket()创建一个套接字socket用来监听 Bind()绑定一个IP地址和一个端口 Listen()开启监听,等待接受客户端的连接
Accept()等待处理客户端的连接请求
客户端(client):
Socket()创建一个套接字socket Connect()想服务器发起连接请求
2.系统流程图
三.系统实现与测试
**新手步骤:
①首先确保虚拟机装有vim编辑器,GCC编译器
②打开terminal(有可视化界面的右键点击打开,无可视化界面的当前界面就是)
③vim client.c ,然后按字母i,输入下面client.c的代码,ESC退出编辑模式。输入冒号+wq(保存)
④同理,vim server.c ,与③相同
⑤用GCC编译器同时编译两个文件。操作:gcc client.c -o client (gcc server.c -o server)
⑥然后同时打开三个terminal,两个运行客户端,一个运行服务端,
⑦文件执行操作: ./client (./server)
⑧注意执行顺序。先打开服务端的执行文件,然后再执行客户端的执行文件
⑨在客户端1和2输入任意文本,服务端都可收到
1.主要代码
client.c
/*file name:client.c*/
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#define _SEVER_IP_ "192.168.1.108"
#define _PORT_ 8080
int main()
{
int sfd,len;
struct sockaddr_in serv_addr;
char buf[1024];
sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
perror("socket fail");
exit(1);
}
else{
printf("client socket success\n");
}
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
inet_pton(AF_INET,_SEVER_IP_,&serv_addr.sin_addr.s_addr);
serv_addr.sin_port = htons(_PORT_);
if(connect(sfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0){
perror("client conect fail");
exit(1);
}
else{
printf("client connect success\n");
}
while(1){
fgets(buf,sizeof(buf),stdin);
int r = write(sfd,buf,strlen(buf));
if(r<0){
perror("client write fail");
exit(1);
}
printf("write=============== %d\n",r);
}
close(sfd);
return 0;
}
server.c
/* filename:server.c*/
#include<stdio.h>
#include<string.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<signal.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/socket.h>
#define SER_PORT 8080
void do_sigchld(int mun)
{
while(waitpid(0,NULL,WNOHANG)>0);
}
int main(void)
{
struct sockaddr_in ser_add,cli_add;
socklen_t cli_add_len;
char buf[2048];
char str[128];
int i,n;
pid_t pid;
sigset_t myset;
sigemptyset(&myset);
sigaddset(&myset,SIGCHLD);
sigprocmask(SIG_BLOCK,&myset,NULL);
printf("SIGCHLD is BLOCK.\n");
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd<0){
perror("socket fail");
exit(1);
}
printf("socket success.\n");
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bzero(&ser_add,sizeof(ser_add));
ser_add.sin_family=AF_INET;
ser_add.sin_addr.s_addr=htonl(INADDR_ANY);
ser_add.sin_port=htons(SER_PORT);
printf("init ser_add success, IP is:%d\t,port is %d\n",ser_add.sin_addr.s_addr,ser_add.sin_port);
int ret =bind(listenfd,(struct sockaddr*)&ser_add,sizeof(ser_add));
if(ret<0){
perror("listen fail");
exit(1);
}
printf("bind success\n");
ret = listen(listenfd,20);
if(ret<0){
perror("listen fail");
exit(1);
}
printf("listen success\n");
printf("Waiting connections...\n");
int k=0;
while(1){
cli_add_len = sizeof(cli_add);
int connfd = accept(listenfd,(struct sockaddr*)&cli_add,&cli_add_len);
if(connfd<0){
perror("connect fail");
exit(1);
}
else{
k++;
printf("创建第 %d 个子进程\n",k);
pid = fork();
if(pid<0){
perror("fork fail\n");
exit(1);
}
else if(pid == 0){
close(listenfd);
while(1){
int len = read(connfd,buf,sizeof(buf));
if(len<0){
perror("read fail");
exit(1);
}
else if(len==0){
printf("客户端断开连接\n");
break;
}
else{
char str[256];
printf("客户端的IP:%s\t,端口:%d\n",inet_ntop(AF_INET,&cli_add.sin_addr,str,sizeof(str)),ntohs(cli_add.sin_port));
int len_w=write(STDOUT_FILENO,buf,len);
if(len_w<0){
perror("write fail");
exit(1);
}
}
}
close(connfd);
return 0;
}
else{
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = do_sigchld;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD,&act,NULL);
sigprocmask(SIG_UNBLOCK,&myset,NULL);
close(connfd);
}
}
}
return 0;
}
运行截图