厦门大学计算机网络实验三

准备工作

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的简易聊天室

  1. 首先我了解了线程相关知识。
  • 守护线程:如果有一个线程必须设置为无限循环,那么该线程不结束,意味着整个python程序就不能结束,那为了能够让python程序正常退出,将这类无限循环的线程设置为守护线程,当程序当中仅仅剩下守护线程时,python程序就能够正常退出,不必关心这类线程是否执行完毕,这就是守护线程的意义。

  • 因为客户端需要一边发送,一边接受。就创建一个守护线程来控制接受信息。

  1. 成果图:

    服务器:

    客户端,检测用户名登录

    客户端,聊天内容

    客户端退出:

    捕获的异常(服务器突然关闭)

  2. 实现:客户端

    1. 创建两个线程,其中设定接受信息为守护线程

    2. 检测昵称

    3. 异常捕获

    4. 实现:服务器

      1. 初始化
      2. 接受信息
      3. 群发消息
      4. 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;    //portfor (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;    //portfor (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  localhostserver_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;    //portfor (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 sysclass Server:user_info = {}_ServerOpen = Truedef __init__(self,server_port):self.serverPort = server_portself.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('-------服务器中断--------')breakdef send_msg(self):while self._ServerOpen:try:data_ifno = input()if data_ifno == 'exit':self._ServerOpen = Falseprint('服务器关闭中')self.send_to_all('服务器已关闭请下线')breakexcept (KeyboardInterrupt,EOFError):print('------服务器中断--------')breakdef 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 timeclass Client():def __init__(self,server_post):#    self.clientPort = client_postself.serverPort = server_postself.udpSocket = socket(AF_INET,SOCK_DGRAM)#    self.userName = nameself.thread_recv = Thread(target=self.recvMsg,daemon=True)self.thread_send = Thread(target=self.sendMsg)def start(self):name = input('请输入昵称:')message = 'login '+nameself.udpSocket.sendto(message.encode(), self.serverPort)while True:try:recvData,destIP = self.udpSocket.recvfrom(1024)if recvData.decode() == 'OK':print('欢迎来到聊天室,退出聊天室请输入exit')breakelse:name = input('该昵称已被占用,请重新输入昵称:')message = 'login '+nameself.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()breakprint(recvData.decode())except (ConnectionResetError,TypeError):print('------服务器连接失败--------')def sendMsg(self):while True:dataInfo = input()self.udpSocket.sendto(dataInfo.encode(),self.serverPort)if dataInfo == 'exit':print('-------连接关闭-------')breakif if__name__=='__main__':postAdd = input('请输入服务器的IP地址: ')postNum = int(input('请输入服务器的端口号: '))client = Client((postAdd,postNum))client.start()

实验总结

  1. 实验的各种技术API课上都没有学过,算是一个完完全全靠自学的实验了。还是很有挑战的,尤其是udp聊天室。实验花费了我整整一天。
  2. 为了处理多个客户端并行运行,我们需要使用到子进程或者是多线程。所以还学习了一下进程和线程的知识。
  3. 聊天室我看了一份相关代码是面向对象写法,我也就写的面向对象。这样写出来的代码结构看起来确实很舒服。
  4. 我现在还有客户端无法^C直接退出程序,是因为还有线程在运行的问题,现在还找不到答案。希望以后对线程系统调用和它内在逻辑了解更深后能够解决这个问题。

厦门大学计算机网络实验三相关推荐

  1. 计算机网络实验设计应用题,计算机网络实验三实验报告.doc

    计算机网络实验三实验报告 实验综合成绩 (百分制) 实验评阅教师签名其中实验态度优良中及格不及格实验报告优良中及格不及格 实 验 报 告 实验时间: 2015年 12 月 24日 实验运行环境 win ...

  2. 计算机网络实验三—— Cisco Packet Tracer 实验

    计算机网络实验三-- Cisco Packet Tracer 实验 CPT 软件使用简介 一.直接连接两台 PC 构建 LAN 二.用交换机构建 LAN 机器名 IP 子网掩码 ✎ 问题 ✎ 试一试 ...

  3. 计算机网络实验三 cpt

    计算机网络实验三 Cisco Packet Tracer 实验 本部分实验共有 15 个,需使用 Cisco Packet Tracer 软件完成. 请大家先了解 VLSM.CIDR.RIP.OSPF ...

  4. 计算机网络与协议实验VLAN配置,计算机网络实验三虚拟局域网vlan划分与配置

    计算机网络实验三虚拟局域网vlan划分与配置 (5页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.90 积分 计算机网络原理计算机网络原理 实验报告 ...

  5. 计算机网络系统结构分析 pdf,计算机网络(实验三:数据包结构分析).pdf

    <计算机网络>课程实验报告 实验三:数据包结构分析 姓名 院系 学号 任课教师 指导教师 实验地点 计 704 实验时间 五 7-8 出勤.表现得分 实验报告 实验课表现 (10) 实验总 ...

  6. 计算机网络实验三 路由协议的配置

    一.实验目的 1.掌握静态路由协议的配置; 2.掌握RIP协议特点和其配置方式: 3.掌握OSPF协议的特点和其配置方式: 二.实验要求 1.掌握静态路由协议的配置; (1) 配置一个互联网络,可如下 ...

  7. 西南科技大学计算机网络实验四,西南科技大学计算机网络-实验三.pdf

    计算机网络实验报告 实验三 实验名称: 基本 VLAN 间路由 学 号: 学生姓名: 班 级: 指导教师: 实验地点: 评 分: 一.实验目的  在交换 LAN 和路由器上执行基本配置 务  在所 ...

  8. 计算机网络课程设计子网划分,计算机网络实验三   子网掩码与划分子网

    实验三子网掩码与划分子网 一.实验目的 (1)掌握子网掩码的算法 (2)掌握用子网掩码划分子网以及动手搭建子网的方法 (3)熟悉模拟软件Packet Tracert5.3的使用 二.实验仪器设备及软件 ...

  9. 计算机网络实验三——IP网络规划与路由设计

    一.实验目的 1.区别节点.网段.广播三种类型IP地址 2.掌握IP子网掩码的两种表示方法 3.明确IP网关含义 4.掌握IP子网划分.网络规划的基本方法 5.熟悉组网仿真工具GNS3使用方法 6.学 ...

最新文章

  1. SAP Ariba——全球最大的采购服务平台
  2. 黄聪:第2章 并发操作的一致性问题 (2)
  3. python课程设计总结1000-编程小白学习python总结文章(一)
  4. 五十四、Java日期Date,LocalDate类以及格式化输出
  5. python png 背景透明_python – Pygame:在png图像中将所有白色像素转换为完全透明...
  6. 笔记 | 《机器学习》半监督学习
  7. 软件测试--接口流程化测试
  8. c语言220程序,电赛必备220个C语言实例源码分享
  9. 美国政府继续紧盯中兴,并可能剑指华为
  10. Java并发包实际应用_Java并发包之核心AQS
  11. 推荐系统的4个方面完全总结
  12. 【荣耀内推】2023届荣耀校招开启啦
  13. Lighttpd介绍
  14. 商业插画师走尺印象:只为做生活的设计师
  15. redis rua解决库存问题_Redis事务
  16. 【学术相关】2021年国家自然科学基金高校立项情况最新数据
  17. linux的rio包在哪个头文件,[Linux] RIO C++封装
  18. Python爬取新浪微博热搜榜
  19. python 06 基本数据类型 tuple
  20. Android RxJava操作符的学习---功能性操作符--(有条件)网络请求轮询(结合Retrofit)

热门文章

  1. linux全局查找字符串,linux全局搜索命令
  2. react 图片剪切(react-easy-crop)
  3. QueryDSL 关于Q类找不到的问题
  4. 汇编语言与微机原理 期末半开卷复习整理(下)
  5. java xml中的冒号_带冒号的xml元素名称
  6. anaconda linux卸载,Linux上Anaconda的卸载
  7. Springboot美食汇开放平台8ob70计算机毕业设计-课程设计-期末作业-毕设程序代做
  8. JavaScript实现点击按钮显示当前时间
  9. 【微信小程序】随机点名系统(点击开始滚动名字点击结束按钮结束滚动)
  10. 【2022】年度总结——彼此当年少 莫负好时光