一、实验目的
(一)掌握面向连接的(TCP)Socket通信技术
(二)掌握无连接的(UDP)Socket通信技术
(三)掌握基本的服务器多客户端模式编程方法
二、实验内容
面向连接(TCP)Socket通信:
编写一个server.c, 该服务程序运行在树莓派或qemu,服务程序 将接收来自不同客户端的服务请求并作出相应回答;
编写3个客户端程序client1.c,client2.c,client3.c,分别向服务 程序请求服务并接收来自服务程序的应答。
无连接(UDP)Socket通信 :
编写一个server.c, 该服务程序运行在树莓派或qemu,服务程序将接收来自不同客户端的服务请求并作出相应回答;
编写2个客户端程序client1.c,client2.c,分别向服务程序请求服务并接收来自服务器的应答。
三、实验过程与结果
TCP:
TCP 中,套接字是一对一的关系。如要向 10 个客户端提供服务,那么除了负责监听的套接字外,还需要创建 10 套接字(在作业中通过创建子进程实现,父进程的client套接字直接回收)。但在 UDP 中,不管是服务器端还是客户端都只需要 1 个套接字。
Server(树莓派)端:
Client(PC)端:
UDP:
UDP 不是点对点,不存在请求连接和受理过程,所以也不需要创建子进程一一对应,因此在某种意义上无法明确区分服务器端和客户端,只是因为其提供服务而称为服务器端。
Server(树莓派)端:
客户(PC)端:
四、未解决的问题
想设置某权限指令比如,123,发出此特定指令可以远程关闭server端的tcp服务:
但是在server程序中,break之后程序依然处于阻塞状态。
原因是因为创造了子进程之后,父进程和子进程都会执行,只处理了子进程,而父进程没有处理。起初解决方案是通过获取父进程的pid,然后结束指定pid的进程,但是这样原先的子进程未消失从而变成了孤儿进程。
附实验代码:
TCP
server0.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
int main()
{
int data,datalen; //data为缓冲
int sockfd_s,sockfd_c,address_len;
struct sockaddr_in address_s,address_c;
// 建立socket
sockfd_s = socket(AF_INET, SOCK_STREAM, 0);
address_s.sin_family = AF_INET; //地址族,设置为AF_INET
address_s.sin_addr.s_addr = inet_addr("192.168.137.251"); //IP地址,设为主机地址
address_s.sin_port = htons(9999); //端口,使用bind函数时需要将sin_port转换成为高位字节优先
//套接字与IP、端口绑定
bind(sockfd_s,(struct sockaddr*)&address_s,sizeof(address_s));
//监听,等待最大数为20
listen(sockfd_s,20);
//接收client请求
address_len = sizeof(address_c);
datalen = sizeof(data);
while(1){
sockfd_c = accept(sockfd_s,(struct sockaddr*)&address_c,&address_len);
printf("此时进行至accpet\n");
sleep(1);
pid_t pid = fork();
if(pid == -1){
perror("fail to create fork!\n");
exit(EXIT_FAILURE);
}else if(pid == 0){
recv(sockfd_c,&data,datalen,0);
printf("server:data from client = %d\n",data);
data+=100;
send(sockfd_c,&data,datalen,0);
if(data == 223){
//kill(getppid(),SIGINT);
printf("接受到data123,通信结束。\n");
exit(0);
//break;
}
//exit(EXIT_SUCCESS);
}else{
recv(sockfd_c,&data,datalen,0);
close(sockfd_c);
printf("sockfd_c has closed,sockfd_s:%d.\n",sockfd_s);
}
printf("data:%d\n",data);
}
close(sockfd_c);
close(sockfd_s);
return 0;
}
client1.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(){
int sockfd, datalen, data = 100;
struct sockaddr_in address;
sockfd = socket(AF_INET,SOCK_STREAM,0);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("192.168.137.251");//树莓派地址
address.sin_port = htons(9999);
if(connect(sockfd,(struct sockaddr*)&address,sizeof(address))==-1){
perror("client1:failed to connect!\n");
exit(1);
}
while(1){
datalen = sizeof(data);
send(sockfd, &data, datalen, 0);
recv(sockfd, &data, datalen, 0);
printf("client1:data from server = %d\n",data);
exit(1);
}
close(sockfd);
}
UDP
server0.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
int main()
{ int flag = 0;
int data,datalen; //data为缓冲
int sockfd_s,sockfd_c,address_len;
struct sockaddr_in address_s,address_c;
// 建立socket
sockfd_s = socket(AF_INET, SOCK_DGRAM, 0);
address_s.sin_family = AF_INET; //地址族,设置为AF_INET
address_s.sin_addr.s_addr = inet_addr("192.168.137.251"); //IP地址,设为主机地址
address_s.sin_port = htons(9999); //端口,使用bind函数时需要将sin_port转换成为高位字节优先
//套接字与IP、端口绑定
bind(sockfd_s,(struct sockaddr*)&address_s,sizeof(address_s));
//接收client请求
address_len = sizeof(address_c);
datalen = sizeof(data);
while(1){
int adlen = sizeof(address_c);
recvfrom(sockfd_s,&data,datalen,0,(struct sockaddr*)&address_c, &adlen);
printf("server:data from client = %d\n",data);
data+=100;
sendto(sockfd_s,&data,datalen,0,(struct sockaddr*)&address_c,sizeof(address_c));
}
close(sockfd_c);
close(sockfd_s);
return 0;
}
client0.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(){
int sockfd, datalen, data = 0;
struct sockaddr_in address,address_s;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
//address is the client address.
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(9999);
//address_s is the server address.
address_s.sin_family = AF_INET;
address_s.sin_addr.s_addr = inet_addr("192.168.137.251");
address_s.sin_port = htons(9999);
datalen = sizeof(data);
int adlen = sizeof(address_s);
sendto(sockfd, &data, datalen, 0, (struct sockaddr*)&address_s, sizeof(address_s));
recvfrom(sockfd, &data, datalen, 0, (struct sockaddr*)&address_s, &adlen);
printf("client0:data from server = %d\n",data);
exit(1);
close(sockfd);
}