• 网络编程day6作业


    完成网络聊天室编写

    ser

    1. #define ERR_MSG(msg) do{\
    2. fprintf(stderr,"__%d__",__LINE__);\
    3. perror(msg);\
    4. }while(0)
    5. #define IP "127.0.0.1"
    6. #define PORT 6666
    7. //创建链表
    8. Linklistptr list_create();
    9. Linklistptr node_buy(datatype e);
    10. int list_insert_head(Linklistptr L,datatype e);
    11. int chat_login(Linklistptr L,int sfd,msg_t msg,datatype e);
    12. int chat_enter(Linklistptr l,int sfd,msg_t msg,datatype e);
    13. int chat_exit(Linklistptr L,int sfd,msg_t msg,datatype e);
    14. typedef struct sockaddr_in datatype;//类型重命名
    15. //创建信息结构体
    16. typedef struct msg
    17. {
    18. char type;//操作码 'L'登录 'C'群聊 'Q'退出
    19. char name[20];
    20. char text[128];
    21. }msg_t;
    22. //创建链表保存地址信息
    23. typedef struct Node
    24. {
    25. union
    26. {
    27. datatype res_addr;//数据域
    28. int len;//头结点数据域
    29. };
    30. struct Node *next;//指针域
    31. }Node, *Linklistptr;
    32. int main(int argc, const char *argv[])
    33. {
    34. //创建报式套接字
    35. int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    36. if(sfd < 0)
    37. {
    38. ERR_MSG("socket");
    39. return -1;
    40. }
    41. printf("socket create success sfd=%d\n",sfd);
    42. //填充接收方的地址信息结构体,给bind函数使用
    43. datatype sin;
    44. sin.sin_family = AF_INET;
    45. sin.sin_port = htons(PORT);
    46. sin.sin_addr.s_addr = inet_addr(IP);
    47. //绑定地址信息结构体
    48. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    49. {
    50. ERR_MSG("bind");
    51. return -1;
    52. }
    53. printf("bind success");
    54. //创建接收地址信息结构体
    55. datatype cin;
    56. socklen_t addrlen = sizeof(cin);
    57. pthread_t tid;
    58. msg_t msg;
    59. //创建一个链表
    60. Linklistptr L = list_create();
    61. if(NULL == L)
    62. {
    63. return -1;
    64. }
    65. while(1)
    66. {
    67. //主线程负责接收并处理
    68. if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &addrlen) < 0)
    69. {
    70. ERR_MSG("recvfrom");
    71. return -1;
    72. }
    73. printf("[%s:%d] : %s\n", msg.name, ntohs(cin.sin_port), msg.text);
    74. switch(msg.type)
    75. {
    76. case 'L'://登录
    77. chat_login(L,sfd,msg,e);
    78. break;
    79. case 'C'://群聊
    80. chat_enter(L,sfd,msg,e);
    81. break;
    82. case 'Q'://退出
    83. chat_exit(L,sfd,msg,e);
    84. break;
    85. default :
    86. printf("输入错误\n");
    87. break;
    88. }
    89. info.sfd=sfd;
    90. info.sin=sin;
    91. //分支线程只负责发送系统信息
    92. if(pthread_create(&tid, NULL, task,(void*)&info) != 0)
    93. {
    94. fprintf(stderr, "pthread_create failed__%d__\n",__LINE__);
    95. return -1;
    96. }
    97. pthread_detach(tid);
    98. }
    99. //关闭文件描述符
    100. if(close(sfd) < 0)
    101. {
    102. ERR_MSG("close");
    103. return -1;
    104. }
    105. return 0;
    106. }
    107. void *task(void *arg)
    108. {
    109. int sfd = ((struct Climsg*)arg)->sfd;
    110. datatype sin = ((struct Climsg*)arg)->sin;
    111. msg_t msg;
    112. msg.type = 'C';
    113. while(1)
    114. {
    115. //从终端获取消息文本
    116. fgets(msg.text, sizeof(msg.text), stdin);
    117. msg.text[strlen(msg.text)-1] = '\0';
    118. //将信息包名定为服务器
    119. strcpy(msg.name,"servce");
    120. if(sendto(sfd , &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    121. {
    122. ERR_MSG("sendto");
    123. }
    124. }
    125. close(sfd);
    126. pthread_exit(NULL);
    127. }
    128. //创建链表
    129. Linklistptr list_create()
    130. {
    131. //从堆区申请一个头结点类型
    132. Linklistptr L = (Linklistptr)malloc(sizeof(Node));
    133. if(NULL == L)
    134. {
    135. printf("创建失败\n");
    136. return NULL;
    137. }
    138. //创建成功后,对节点进行初始化工作
    139. L->len = 0;
    140. L->next = NULL;
    141. return L;
    142. }
    143. //申请节点封装地址信息
    144. Linklistptr node_buy(datatype e)
    145. {
    146. //在堆区申请节点
    147. Linklistptr p = (Linklistptr)malloc(sizeof(Node));
    148. if(NULL == p)
    149. {
    150. printf("申请失败]n");
    151. return NULL;
    152. }
    153. //节点申请成功,封装数据
    154. p->res_addr = e;
    155. p->next = NULL;
    156. return p;
    157. }
    158. //头插
    159. int list_insert_head(Linklistptr L,datatype e)
    160. {
    161. //判断逻辑
    162. if(NULL == L)
    163. {
    164. printf("所给链表不合法\n");
    165. return -1;
    166. }
    167. //调用节点封装数据
    168. Linklistptr p = node_buy(e);
    169. if(NULL == p)
    170. {
    171. return -1;
    172. }
    173. //头插
    174. p->next = L->next;
    175. L->next = p;
    176. //表长变化
    177. L->len++;
    178. return 0;
    179. }
    180. int chat_login(Linklistptr L,int sfd,msg_t msg,datatype e)
    181. {
    182. Linklistptr p=L;//定义一个遍历指针
    183. while(p->next!=NULL)
    184. {
    185. p=p->next;
    186. if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr *)&(p->res_addr),sizeof(p->res_addr))<0)
    187. {
    188. ERR_MSG("sendto");
    189. return -1;
    190. }
    191. }
    192. //头插,将自己的地址存入链表中
    193. list_insert_head(L,e);
    194. return 0;
    195. }
    196. //群聊
    197. int chat_enter(Linklistptr l,int sfd,msg_t msg,datatype e)
    198. {
    199. //定义一个遍历指针
    200. Linklistptr p=L;
    201. while(p->next!=NULL)
    202. {
    203. p=p->next;
    204. //判断链表客户端信息
    205. if(memcmp(&(p->res_addr),&e,sizeof(e))==0)
    206. {
    207. if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr*)&(p->res_addr),sizeof(p->res_addr))<0)
    208. {
    209. ERR_MSG("sendto");
    210. return -1;
    211. }
    212. }
    213. }
    214. return 0;
    215. }
    216. //退出
    217. int chat_exit(Linklistptr L,int sfd,msg_t msg,datatype e)
    218. {
    219. Linklistptr p=L;
    220. while(p->next!=NULL)
    221. {
    222. p=p->next;
    223. //判断链表客户端信息
    224. if(memcmp(&(p->res_addr),&e,sizeof(e))==0)
    225. {
    226. if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr*)&(p->res_addr),sizeof(p->res_addr))<0)
    227. {
    228. ERR_MSG("sendto");
    229. return -1;
    230. }
    231. }
    232. else//此时当前节点的下一个节点保存的就是要退出的成员的信息
    233. {
    234. Linklistptr q=p->next;
    235. if(sendto(sfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&(q->resin), sizeof(q->resin)) < 0)
    236. {
    237. ERR_MSG("sendto");
    238. return -1;
    239. }
    240. p->next=q->next;
    241. free(q);
    242. q=NULL;
    243. }
    244. }
    245. return 0;
    246. }

    cli

    1. #include
    2. #define ERR_MSG(msg) do{\
    3. fprintf(stderr,"__%d__",__LINE__);\
    4. perror(msg);\
    5. }while(0)
    6. #define IP "127.0.0.1"
    7. #define PORT 4399
    8. typedef struct sockaddr_in datatype;//类型重命名
    9. //创建信息结构体
    10. typedef struct msg
    11. {
    12. char type;//操作码 'L'登录 'C'群聊 'Q'退出
    13. char name[20];
    14. char text[128];
    15. }msg_t;
    16. struct Climsg
    17. {
    18. int cfd;
    19. datatype cin;
    20. msg_t msg;
    21. };
    22. void *task(void *arg);
    23. int main(int argc, const char *argv[])
    24. {
    25. //创建报式套接字
    26. int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    27. if(cfd < 0)
    28. {
    29. ERR_MSG("socket");
    30. return -1;
    31. }
    32. printf("socket create success cfd=%d\n",cfd);
    33. //填充接收方的地址信息结构体,给sendto函数使用
    34. datatype sin;
    35. sin.sin_family = AF_INET;
    36. sin.sin_port = htons(PORT);
    37. sin.sin_addr.s_addr = inet_addr(IP);
    38. //创建接收地址信息结构体
    39. datatype cin;
    40. socklen_t c_addrlen = sizeof(cin);
    41. pthread_t tid;
    42. msg_t msg;
    43. memset(&msg, 0, sizeof(msg_t));
    44. struct Climsg info;
    45. printf("请输入登录名:");
    46. fgets(msg.name, sizeof(msg.name), stdin);
    47. msg.name[strlen(msg.name)-1] = '\0';
    48. msg_t msg1=msg;
    49. msg.type = 'L';
    50. strcpy(msg.text,"已进入群聊");
    51. //发送登录请求包
    52. if(sendto(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    53. {
    54. ERR_MSG("sendto");
    55. return -1;
    56. }
    57. printf("登陆成功\n");
    58. info.cfd = cfd;
    59. info.cin = sin;
    60. info.msg = msg;
    61. while(1)
    62. {
    63. memset(&msg, 0, sizeof(msg));
    64. if(recvfrom(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&cin, &c_addrlen) < 0)
    65. {
    66. ERR_MSG("recvfrom");
    67. return -1;
    68. }
    69. if(strcmp(msg.name, msg1.name) == 0)
    70. {
    71. break;
    72. }
    73. printf("[%s] : %s\n", msg.name, msg.text);
    74. if(pthread_create(&tid, NULL, task, (void *)&info) != 0)
    75. {
    76. ERR_MSG("pthread_create");
    77. return -1;
    78. }
    79. //将分支线程设置为分离态
    80. pthread_detach(tid);
    81. }
    82. //关闭套接字描述符
    83. close(cfd);
    84. return 0;
    85. }
    86. //线程体函数
    87. void *task(void *arg)
    88. {
    89. int cfd = ((struct Climsg*)arg)->cfd;
    90. datatype sin = ((struct Climsg*)arg)->cin;
    91. msg_t msg = ((struct Climsg*)arg)->msg;
    92. while(1)
    93. {
    94. //清空文本内容
    95. bzero(msg.text, sizeof(msg.text));
    96. //从终端获取数据
    97. fgets(msg.text, sizeof(msg.text), stdin);
    98. msg.text[strlen(msg.text)-1] = '\0';
    99. msg.type = 'C';
    100. if(strcmp(msg.text,"quit") == 0)
    101. {
    102. msg.type = 'Q';
    103. strcpy(msg.text, "已下线");
    104. }
    105. if(sendto(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    106. {
    107. ERR_MSG("sendto");
    108. }
    109. if(strcmp(msg.text, "已下线") == 0)
    110. {
    111. break;
    112. }
    113. }
    114. close(cfd);
    115. pthread_exit(NULL);
    116. }

    思维导图https://mubu.com/app/edit/home/5fnWgXpb5GT#m

  • 相关阅读:
    【疯狂Java】数组
    SpringBoot+Kafka
    CSS3自定义滚动条的颜色等样式 - hover显示切换 - 伪类::-webkit-scrollbar的样式控制
    vulhub中Apache Log4j2 lookup JNDI 注入漏洞(CVE-2021-44228)
    短信的信令过程
    论文《Unsupervised Dialog Structure Learning》笔记:详解DD-VRNN
    uniapp navigateTo url路由传参(传递对象)
    PHP:命名空间必知必会
    【HMS Core】华为分析服务通过REST方式上报用户行为,控制台为何无法查询到相关数据?
    ElasticSearch三种分页对比
  • 原文地址:https://blog.csdn.net/m0_59031281/article/details/132656609