厦门大学计算机网络第三次实验
准备工作
1.安装本地echo服务,监听7号端口。我使用的是ubuntu虚拟机,修改的文件与centos7略有不同。
可以看到我已经配置好了。(不会配置的可以搜索一下,ubuntu打开echo标准服务)
2.在ubuntu虚拟机编程太难受了,vscode使用ssh连接,在vscode里code和调试。
字符串逆序回送(TCP迭代)
1.成果展示:
客户端
服务器:
第一个客户端正常收发
第二个客户端等待
收到bye后,立刻开始处理第二个客户端:
2.具体实现
在服务器端使用双重循环,内部有字符串处理
客户端使用单个循环。
我还在对运行时参数做了处理,这样服务器和客户端就可以自定地址和端口号。
3.为什么ip地址和端口号需要字节顺序转换?
htnol()函数其实很好理解,就是小端装换成大端,因为网络地址是大端,但计算机内存不一定与之一样,所以必须要转换。
字符串逆序回送(TCP并发)
1.先看实现成果
服务器端:
两个客户端
2.实验报告要求的三客户端:
3.实现细节
- 需要在子进程中关掉监听socket
- 在父进程关闭数据socket
- 一定要记得清楚僵尸进程
4.ppt问题。服务器accept之后会返回一个用于传输数据的socket,调用fork()会使父子进程同时拥有此socket描述符,父进程分支中是否需要关闭该socket?
答案是需要
若不关闭,在退出客户端后,还有多个网络连接在CLOSE_WAIT
基于UDP socket的简易聊天室
- 首先我了解了线程相关知识。
-
守护线程:如果有一个线程必须设置为无限循环,那么该线程不结束,意味着整个python程序就不能结束,那为了能够让python程序正常退出,将这类无限循环的线程设置为守护线程,当程序当中仅仅剩下守护线程时,python程序就能够正常退出,不必关心这类线程是否执行完毕,这就是守护线程的意义。
-
因为客户端需要一边发送,一边接受。就创建一个守护线程来控制接受信息。
-
成果图:
服务器:
客户端,检测用户名登录
客户端,聊天内容
客户端退出:
捕获的异常(服务器突然关闭)
-
实现:客户端
-
创建两个线程,其中设定接受信息为守护线程
-
检测昵称
-
异常捕获
-
实现:服务器
- 初始化
- 接受信息
- 群发消息
- main
-
源代码
-
server1.c
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <error.h> #include <stdlib.h> int main(int argc, char *argv[]) { int server_sock_listen, server_sock_data; struct sockaddr_in server_addr; char recv_msg[255]; char send_msg[255]; /* 创建socket */ server_sock_listen = socket(AF_INET, SOCK_STREAM, 0); int i,port=0; //port for (i=0;i< strlen(argv[2]);i++) { if(argv[2][i] != 0){ port = port*10 + argv[2][i] - '0'; } } /* 指定服务器地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); if(strcmp(argv[1],"localhost")==0){ server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY表示本机所有IP地址 } else { server_addr.sin_addr.s_addr = htonl(inet_addr(argv[1])); } memset(&server_addr.sin_zero, 0, sizeof(server_addr.sin_zero)); //零填充 /* 绑定socket与地址 */ bind(server_sock_listen, (struct sockaddr *)&server_addr, sizeof(server_addr)); /* 监听socket */ listen(server_sock_listen, 0); printf("%s is listening:\n",argv[1]); while(1) { server_sock_data = accept(server_sock_listen, NULL, NULL); printf("Accept.....\n"); while(1){ /* 接收并显示消息 */ memset(recv_msg, 0, sizeof(recv_msg)); //接收数组置零 memset(send_msg, 0, sizeof(send_msg)); recv(server_sock_data, recv_msg, sizeof(recv_msg), 0); printf("Recv: %s\n", recv_msg); if(strcmp(recv_msg,"bye")==0) break; int len = strlen(recv_msg); for( i = 0;i < len; i++) { send_msg[i] = recv_msg[len - 1 - i]; } printf("Send: %s\n", send_msg); /* 发送消息 */ send(server_sock_data, send_msg, strlen(send_msg), 0); } /* 关闭数据socket */ close(server_sock_data); } /* 关闭监听socket */ close(server_sock_listen); return 0; }
-
client1.c
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <error.h> #include <stdlib.h> int main(int argc, char *argv[]) { int client_sock; struct sockaddr_in server_addr; char send_msg[1000]; char recv_msg[255]; /* 创建socket */ client_sock = socket(AF_INET, SOCK_STREAM, 0); /* 指定服务器地址 */ int i,port=0; //port for (i=0;i< strlen(argv[2]);i++) { if(argv[2][i] != 0){ port = port*10 + argv[2][i] - '0'; } } if(!strcmp(argv[1],"localhost")) { //input is localhost server_addr.sin_addr.s_addr = inet_addr("192.168.238.128"); } else{ server_addr.sin_addr.s_addr = inet_addr(argv[1]); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); memset(server_addr.sin_zero, 0, sizeof(server_addr.sin_zero)); //零填充 /* 连接服务器 */ connect(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); /* 发送消息 */ while(1){ printf("Myself: "); scanf("%s", send_msg); send(client_sock, send_msg, strlen(send_msg), 0); if (strcmp(send_msg,"bye")==0) { break; } /* 接收并显示消息 */ memset(recv_msg, 0, sizeof(recv_msg)); //接收数组置零 recv(client_sock, recv_msg, sizeof(recv_msg), 0); printf("Recv: %s\n", recv_msg); } /* 关闭socket */ close(client_sock); return 0; }
-
server2.c
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <error.h> #include <stdlib.h> #include <signal.h> #include <sys/wait.h> int main(int argc, char *argv[]) { int server_sock_listen, server_sock_data; struct sockaddr_in server_addr; char recv_msg[255]; char send_msg[255]; /* 创建socket */ server_sock_listen = socket(AF_INET, SOCK_STREAM, 0); int i,port=0; //port for (i=0;i< strlen(argv[2]);i++) { if(argv[2][i] != 0){ port = port*10 + argv[2][i] - '0'; } } /* 指定服务器地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); if(strcmp(argv[1],"localhost")==0){ server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY表示本机所有IP地址 } else { server_addr.sin_addr.s_addr = htonl(inet_addr(argv[1])); } memset(&server_addr.sin_zero, 0, sizeof(server_addr.sin_zero)); //零填充 /* 绑定socket与地址 */ bind(server_sock_listen, (struct sockaddr *)&server_addr, sizeof(server_addr)); /* 监听socket */ listen(server_sock_listen, 0); printf("%s is listening:\n",argv[1]); struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); while(1) { server_sock_data = accept(server_sock_listen, (struct sockaddr*)&client_addr, &client_len); signal(SIGCHLD, SIG_IGN); pid_t pid = fork(); if(pid < 0){ printf("error in fork!\n"); } else if (pid == 0) { close(server_sock_listen); while(1){ /* 接收并显示消息 */ memset(recv_msg, 0, sizeof(recv_msg)); //接收数组置零 memset(send_msg, 0, sizeof(send_msg)); char ip[64]; recv(server_sock_data, recv_msg, sizeof(recv_msg), 0); printf("Recv: %s\n", recv_msg); printf("Accept from %s : %d\n",inet_ntop(AF_INET, &client_addr.sin_addr.s_addr,ip,sizeof(ip)),ntohs(client_addr.sin_port)); if(strcmp(recv_msg,"bye")==0) { printf("Close from %s : %d\n",inet_ntop(AF_INET, &client_addr.sin_addr.s_addr,ip,sizeof(ip)),ntohs(client_addr.sin_port)); break; } int len = strlen(recv_msg); for( i = 0;i < len; i++) { send_msg[i] = recv_msg[len - 1 - i]; } printf("Send: %s\n", send_msg); /* 发送消息 */ send(server_sock_data, send_msg, strlen(send_msg), 0); } return 0; } else{ /* 关闭数据socket */ close(server_sock_data); } } /* 关闭监听socket */ close(server_sock_listen); return 0; }
-
serverchatroom.py
from socket import * from threading import Thread import sys class Server: user_info = {} _ServerOpen = True def __init__(self,server_port): self.serverPort = server_port self.udpSocket = socket(AF_INET,SOCK_DGRAM) self.udpSocket.bind(self.serverPort) self.thread_recv = Thread(target=self.recv_msg) self.thread_send = Thread(target=self.send_msg) def recv_msg(self): while True: try: recv_data,dest_ip = self.udpSocket.recvfrom(1024) if not self._ServerOpen: self.udpSocket.sendto('exit'.encode(), dest_ip) name = self.user_info[dest_ip] self.sent_to_all('系统: %s 已退出聊天'%name) while len(self.user_info): recv_data,dest_ip = self.udpSocket.recvfrom(1024) self.udpSocket.sendto('exit'.encode(),dest_ip) name = self.user_info[dest_ip] del self.user_info[dest_ip] self.send_to_all(' 系统:%s已退出聊天'%name) print('服务器已关闭') self.udpSocket.close() break #处理登录名 info_list = str(recv_data.decode()).split(' ') if info_list[0] == 'login': if info_list[1] not in self.user_info.values(): self.udpSocket.sendto('OK'.encode(),dest_ip) self.send_to_all('系统:%s进入聊天室'%info_list[1]) self.user_info[dest_ip] = info_list[1] else: self.udpSocket.sendto('Used name'.encode(), dest_ip) elif info_list[0] == 'exit' : message = '%s 退出了聊天室'%self.user_info[dest_ip] self.send_to_all(message) del self.user_info[dest_ip] else: name = self.user_info[dest_ip] message = name + ': '+ recv_data.decode() self.send_to_all(message) except (KeyboardInterrupt,EOFError): print('-------服务器中断--------') break def send_msg(self): while self._ServerOpen: try: data_ifno = input() if data_ifno == 'exit': self._ServerOpen = False print('服务器关闭中') self.send_to_all('服务器已关闭请下线') break except (KeyboardInterrupt,EOFError): print('------服务器中断--------') break def start(self): print('服务器已启动,ip地址:'+self.serverPort[0]+'端口号:%d' %self.serverPort[1]) self.thread_recv.start() self.thread_send.start() self.thread_recv.join() self.thread_send.join() def send_to_all(self,message): for i in self.user_info.keys(): self.udpSocket.sendto(message.encode(),i) if __name__=='__main__': port = int(input('请输入要绑定的端口号')) server1 = Server(('192.168.1.101', port)) server1.start()
-
clientchatroom.py
from socket import * from threading import Thread from requests.exceptions import ConnectionError, ReadTimeout import time class Client(): def __init__(self,server_post): # self.clientPort = client_post self.serverPort = server_post self.udpSocket = socket(AF_INET,SOCK_DGRAM) # self.userName = name self.thread_recv = Thread(target=self.recvMsg,daemon=True) self.thread_send = Thread(target=self.sendMsg) def start(self): name = input('请输入昵称:') message = 'login '+name self.udpSocket.sendto(message.encode(), self.serverPort) while True: try: recvData,destIP = self.udpSocket.recvfrom(1024) if recvData.decode() == 'OK': print('欢迎来到聊天室,退出聊天室请输入exit') break else: name = input('该昵称已被占用,请重新输入昵称:') message = 'login '+name self.udpSocket.sendto(message.encode(),self.serverPort) except (ConnectionResetError,TypeError): print('------服务器连接失败--------') self.thread_recv.start() self.thread_send.start() self.thread_send.join() def recvMsg(self): while True: try: recvData,destIp = self.udpSocket.recvfrom(1024) if recvData.decode() == 'exit' and destIp == self.serverPort: print('client is closed\n') self.udpSocket.close() break print(recvData.decode()) except (ConnectionResetError,TypeError): print('------服务器连接失败--------') def sendMsg(self): while True: dataInfo = input() self.udpSocket.sendto(dataInfo.encode(),self.serverPort) if dataInfo == 'exit': print('-------连接关闭-------') break if if__name__=='__main__': postAdd = input('请输入服务器的IP地址: ') postNum = int(input('请输入服务器的端口号: ')) client = Client((postAdd,postNum)) client.start()