当前位置: 首页 > news >正文

网络编程完结整理

UDP(User Datagram Protocol)

网络编程(一)UDP-CSDN博客

  • 特点
    无连接、数据报文传输、不保证可靠性、不保证顺序,适合轻量级、实时性要求高的场景(如语音、视频、局域网发现)。

  • 编程要点

    • 使用 socket(AF_INET, SOCK_DGRAM, 0) 创建。

    • sendto/recvfrom 配合 sockaddr 结构,指定目标 IP/端口。

    • 由于无连接,不需要三次握手,代码更简单,但要注意丢包和顺序错乱。

  • 应用场景
    实时数据传输、局域网发现、简单的状态广播。


2. 组播(Multicast)与广播(Broadcast)

网络编程(二)组播与广播-CSDN博客

  • 广播(Broadcast)

    • 数据发往局域网内所有设备(255.255.255.255 或子网广播地址)。

    • 特点是无差别“喊话”,接收端只要在同一广播域就能收到。

    • 缺点是会占用带宽,不适合大规模或跨路由使用。

  • 组播(Multicast)

    • 数据发往一个“组地址”(224.0.0.0 ~ 239.255.255.255),只有加入组的主机才会收到。

    • 接收端需要调用 setsockopt 设置 IP_ADD_MEMBERSHIP 加入组播组。

    • 节省带宽,适合一对多场景。

  • 应用场景

    • 广播:ARP 请求、局域网内的发现。

    • 组播:视频会议、股票行情推送、网络电视。


3. TCP(Transmission Control Protocol)

网络编程(三)TCP-CSDN博客

  • 特点
    面向连接、可靠传输、保证顺序、支持流量控制。适合需要可靠性的数据传输。

  • 编程要点

    • 创建 socket:socket(AF_INET, SOCK_STREAM, 0)

    • 服务端:bind → listen → accept,接收客户端连接。

    • 客户端:connect 发起三次握手。

    • 数据传输:send/recv,注意 TCP 是“流”,可能存在粘包/拆包问题,需要自定义协议。

  • 应用场景
    聊天室、文件传输、Web 服务。

4. 原始套接字(Raw Socket)

网络编程(四)原始套接字-CSDN博客

  • 特点

    • 可以绕过 TCP/UDP 协议,直接操作网络层(IP)甚至链路层的数据包。

    • 常用于网络嗅探、协议分析、安全测试。

  • 编程要点

    • 创建:socket(AF_INET, SOCK_RAW, protocol)

    • 通常需要 root/管理员权限。

    • 用户需要自己构造 IP 头、TCP/UDP 头以及数据部分。

    • 抓包工具(如 tcpdump、Wireshark)原理即是基于原始套接字。

  • 应用场景
    协议栈学习、自定义协议、IDS/IPS(入侵检测/防御系统)、网络扫描。


总结

  • UDP:轻量、无连接,适合实时性。

  • 组播/广播:一对多通信的扩展方式;广播无差别喊话,组播“按兴趣订阅”。

  • TCP:面向连接、可靠性高,支撑绝大多数互联网应用。

  • 原始套接字:让开发者直面协议头,适合做网络安全、协议研究。

这四个部分,像是从“普通通信”到“进阶一对多”,再到“可靠传输”,最后“深入内核”的一个学习链路。

相关函数总结:网络编程相关函数-CSDN博客

小作业整理

TCP服务器与小客户端的实现

共用头文件ChatMain.h
#ifndef _CHATMAIN_H
#define _CHATMAIN_H#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#define SERVER_IP_ADDR "192.168.119.53"//192.168.16.96
#define SERVER_PORT (8999)
#define SIZE (1024)
#define FDSIZE (10240)// 全局变量声明,使用 extern 表示在其他地方定义
extern int sockUserFd;
extern int sockServerFd;struct File
{char name[SIZE];char data[FDSIZE];size_t size;
};typedef struct message
{int messageType;       // 0服务器消息,1用户消息,2文件消息char targetname[32];   char note[SIZE];       // 消息内容struct File file;      // 文件 
} MES;typedef struct ClientNode {pthread_t threadid;          // 每个客户端线程IDint userfd;                  // 客户端套接字struct sockaddr_in userAddr; // 客户端地址信息char username[SIZE];         // 存储用户名struct ClientNode *next;     // 链表指针int isFinished;              // 标志位,标记线程是否完成
} CN;extern CN *head;  
extern int clientCount;extern pthread_mutex_t Umutex;
extern pthread_mutex_t listMutex; extern int quitFlag;
extern int sockUserFd;#endif
服务器代码
服务器头文件ServerTask.h
#ifndef _SERVERTASK_H
#define _SERVERTASK_H#include "ChatMain.h"void *functionServer(void * argv);
void *functionUser(void * argv);
void *functionMonitor(void *argv);#endifSFunction.h
#ifndef _SFUNCTION_H
#define _SFUNCTION_H#include "ServerTask.h"void Node(pthread_t threadid, int userfd, struct sockaddr_in userAddr, const char *username);
void deleteNode(pthread_t threadid);
void sendOnlineUsers(int userfd);
void transmitUserMessage(int userfd, MES *URmes, const char *senderName);
void broadcastMessage(const char *message);
void broadcastOnlineUsers(void);#endif 
实现代码ServerMain.c
#include "SFunction.h"int sockUserFd;
int sockServerFd;CN *head = NULL;
int clientCount = 0;pthread_mutex_t Umutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t listMutex = PTHREAD_MUTEX_INITIALIZER;int quitFlag = 0;int main(int argc, char const *argv[])
{sockServerFd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);if (-1 == sockServerFd){perror("socket failed");exit(EXIT_FAILURE);}struct sockaddr_in addr;socklen_t addrlen = sizeof(addr);memset(&addr, 0, addrlen);addr.sin_family = AF_INET;addr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP_ADDR, &addr.sin_addr.s_addr);if (bind(sockServerFd, (const struct sockaddr *)&addr, addrlen)){perror("bind failed");close(sockServerFd);exit(EXIT_FAILURE);}if (listen(sockServerFd, 5)){perror("listen failed");close(sockServerFd);exit(EXIT_FAILURE);}pthread_t pthidMonitor;pthread_create(&pthidMonitor, NULL, functionMonitor, NULL); // 总回收pthread_t pthidServer;pthread_create(&pthidServer, NULL, functionServer, NULL);pthread_join(pthidServer, NULL);pthread_join(pthidMonitor, NULL);close(sockServerFd);return 0;
}ServerTask.c
#include "SFunction.h"void *functionUser(void *argv)
{intptr_t userfd = (intptr_t)argv;char myname[SIZE] = {0};pthread_mutex_lock(&listMutex);CN *t = head;while (t != NULL){if (t->userfd == userfd){strncpy(myname, t->username, SIZE - 1);break;}t = t->next;}pthread_mutex_unlock(&listMutex);MES URmes;while (1){memset(&URmes, 0, sizeof(MES));int bytesRead = read(userfd, &URmes, sizeof(MES));if (bytesRead <= 0){printf("用户 %s 已断开连接\n", myname);break;}if (URmes.messageType == 1){printf("[私聊] %s -> %s: %s\n", myname, URmes.targetname, URmes.note);}else if (URmes.messageType == 2){printf("[文件] %s -> %s: %s (大小: %zu bytes)\n",myname, URmes.targetname, URmes.file.name, URmes.file.size);}transmitUserMessage(userfd, &URmes, myname);}close(userfd);pthread_mutex_lock(&listMutex);CN *temp = head;while (temp != NULL){if (temp->threadid == pthread_self()){temp->isFinished = 1;break;}temp = temp->next;}pthread_mutex_unlock(&listMutex);char notice[SIZE];snprintf(notice, SIZE, "用户 %s 下线了", myname);broadcastMessage(notice);return NULL;
}void *functionServer(void *argv)
{while (1){struct sockaddr_in userAddr;socklen_t addrlen = sizeof(struct sockaddr);int userfd = accept(sockServerFd, (struct sockaddr *)&userAddr, &addrlen);if (userfd < 0){perror("accept failed");continue;}char username[SIZE] = {0};int n = read(userfd, username, SIZE);if (n <= 0){printf("接收用户名失败\n");close(userfd);continue;}pthread_t pthid;if (pthread_create(&pthid, NULL, functionUser, (void *)(intptr_t)userfd) != 0){perror("pthread_create failed");close(userfd);continue;}Node(pthid, userfd, userAddr, username);sendOnlineUsers(userfd);char notice[SIZE];snprintf(notice, SIZE, "用户 %s 上线了", username);broadcastMessage(notice);}return NULL;
}void *functionMonitor(void *argv)
{while (1){pthread_mutex_lock(&listMutex);CN *temp = head;CN *prev = NULL;while (temp != NULL){if (temp->isFinished == 1){pthread_join(temp->threadid, NULL);printf("回收线程 %lu\n", temp->threadid);if (prev == NULL){head = temp->next;}else{prev->next = temp->next;}CN *toFree = temp;temp = temp->next;free(toFree);clientCount--;}else{prev = temp;temp = temp->next;}}pthread_mutex_unlock(&listMutex);sleep(1);}return NULL;
}SFunction.c
#include "SFunction.h"void Node(pthread_t threadid, int userfd, struct sockaddr_in userAddr, const char *username)
{CN *newNode = (CN *)malloc(sizeof(CN));if (newNode == NULL){perror("malloc failed");return;}newNode->threadid = threadid;newNode->userfd = userfd;newNode->userAddr = userAddr;strncpy(newNode->username, username, SIZE);newNode->next = NULL;newNode->isFinished = 0;pthread_mutex_lock(&listMutex);if (head == NULL){head = newNode;}else{CN *temp = head;while (temp->next != NULL){temp = temp->next;}temp->next = newNode;}clientCount++;pthread_mutex_unlock(&listMutex);
}void deleteNode(pthread_t threadid)
{pthread_mutex_lock(&listMutex);CN *prev = NULL;CN *current = head;while (current != NULL){if (current->threadid == threadid){if (prev == NULL){head = current->next;}else{prev->next = current->next;}if (head == NULL){head = NULL;}free(current);clientCount--;break;}prev = current;current = current->next;}pthread_mutex_unlock(&listMutex);
}void transmitUserMessage(int userfd, MES *URmes, const char *senderName)
{CN *targetNode = NULL;pthread_mutex_lock(&listMutex);CN *temp = head;while (temp != NULL){if (strcmp(temp->username, URmes->targetname) == 0){targetNode = temp;break;}temp = temp->next;}pthread_mutex_unlock(&listMutex);if (targetNode != NULL){MES sendMsg;memset(&sendMsg, 0, sizeof(MES));sendMsg.messageType = URmes->messageType;strncpy(sendMsg.targetname, senderName, sizeof(sendMsg.targetname) - 1);if (URmes->messageType == 1){snprintf(sendMsg.note, SIZE, "%s: %s", senderName, URmes->note);int total = 0;int size = sizeof(MES);char *ptr = (char *)&sendMsg;while (total < size){int n = write(targetNode->userfd, ptr + total, size - total);if (n <= 0)break;total += n;}}else if (URmes->messageType == 2){sendMsg.file = URmes->file;snprintf(sendMsg.note, SIZE, "%s 发来一个文件: %s", senderName, URmes->file.name);int total = 0;int size = sizeof(MES);char *ptr = (char *)&sendMsg;while (total < size){int n = write(targetNode->userfd, ptr + total, size - total);if (n <= 0)break;total += n;}printf("文件已转发给 %s\n", URmes->targetname);}}else{MES serverMsg;memset(&serverMsg, 0, sizeof(MES));serverMsg.messageType = 0;snprintf(serverMsg.note, SIZE, "目标用户 %s 不在线或不存在。", URmes->targetname);int total = 0;int size = sizeof(MES);char *ptr = (char *)&serverMsg;while (total < size){int n = write(userfd, ptr + total, size - total);if (n <= 0)break;total += n;}}
}void sendOnlineUsers(int userfd)
{pthread_mutex_lock(&listMutex);CN *iter = head;MES mes;memset(&mes, 0, sizeof(MES));mes.messageType = 0; // 服务器消息while (iter != NULL){snprintf(mes.note, SIZE, "在线用户: %s", iter->username);write(userfd, &mes, sizeof(MES));iter = iter->next;}pthread_mutex_unlock(&listMutex);
}void broadcastMessage(const char *message)
{pthread_mutex_lock(&listMutex);CN *temp = head;while (temp != NULL){MES serverMsg;memset(&serverMsg, 0, sizeof(MES));serverMsg.messageType = 0; // 服务器消息strncpy(serverMsg.note, message, SIZE - 1);int total = 0;int size = sizeof(MES);char *ptr = (char *)&serverMsg;while (total < size){int n = write(temp->userfd, ptr + total, size - total);if (n <= 0){// 写失败跳过break;}total += n;}temp = temp->next;}pthread_mutex_unlock(&listMutex);
}
客户端代码
客户端头文件
UserTask.h
#ifndef _USERTASK_H
#define _USERTASK_H
#include "ChatMain.h"void *functionRead(void *argv);
void *functionWrite(void *argv);#endifUFunction.h
#ifndef _UFUNCTION_H
#define _UFUNCTION_H
#include "UserTask.h"void ShowMenu(void);
void messageSend(MES *UWmes);
void fileSend(MES *UWmes);
void cleanStdin(void);#endif
客户端代码UserMain.c
#include "UFunction.h"int sockUserFd;
pthread_mutex_t Umutex = PTHREAD_MUTEX_INITIALIZER;
int quitFlag = 0;int main(int argc, char const *argv[])
{sockUserFd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);if (-1 == sockUserFd){perror("socket failed");exit(EXIT_FAILURE);}struct sockaddr_in addr;socklen_t addrlen = sizeof(addr);memset(&addr, 0, addrlen);addr.sin_family = AF_INET;addr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP_ADDR, &addr.sin_addr.s_addr);if (connect(sockUserFd, (const struct sockaddr *)&addr, addrlen)){perror("connect failed");close(sockUserFd);exit(EXIT_FAILURE);}char username[SIZE];printf("请输入用户名:");fgets(username, SIZE, stdin);username[strcspn(username, "\n")] = 0;write(sockUserFd, username, SIZE);pthread_t pthidRead, pthidWrite;pthread_create(&pthidRead, NULL, functionRead, NULL);pthread_create(&pthidWrite, NULL, functionWrite, NULL);pthread_join(pthidWrite, NULL);  pthread_join(pthidRead, NULL);   printf("客户端已退出。\n");return 0;
}UserTask.c
#include "UFunction.h"void *functionRead(void *argv)
{MES URmes;while (1){int total = 0;int size = sizeof(MES);char *ptr = (char *)&URmes;while (total < size){int n = read(sockUserFd, ptr + total, size - total);if (n <= 0){if (!quitFlag)printf("读取数据失败或连接已关闭\n");return NULL;  }total += n;}if (URmes.messageType == 0)  {printf("[服务器消息] %s\n", URmes.note);}else if (URmes.messageType == 1){printf("[私聊] %s: %s\n", URmes.targetname, URmes.note);}else if (URmes.messageType == 2){printf("[文件] 来自 %s 的文件: %s (大小: %zu bytes)\n",URmes.targetname, URmes.file.name, URmes.file.size);}}return NULL;
}void *functionWrite(void *argv)
{MES UWmes;char input[SIZE];int opt;while (1){memset(&UWmes, 0, sizeof(MES));ShowMenu();printf("请选择操作: ");fgets(input, SIZE, stdin);if (sscanf(input, "%d", &opt) != 1){printf("输入错误,请重新输入数字\n");continue;}switch (opt){case 1:  // 发送消息messageSend(&UWmes);break;case 2:  // 发送文件fileSend(&UWmes);break;case 3:  // 退出printf("正在退出客户端...\n");quitFlag = 1;shutdown(sockUserFd, SHUT_RD);  // 让 functionRead 中 read 返回 0return NULL;  default:printf("没有这个选项\n");break;}}
}UFunction.c
#include "UFunction.h"void ShowMenu(void)
{printf("1.发送消息  ");printf("2.发送文件  ");printf("3.退出\n\n");
}void messageSend(MES *UWmes)
{pthread_mutex_lock(&Umutex);char targetUsername[SIZE];printf("请输入目标用户名:");if (!fgets(targetUsername, SIZE, stdin)){printf("输入错误\n");pthread_mutex_unlock(&Umutex);return;}targetUsername[strcspn(targetUsername, "\n")] = 0;if (strcmp(targetUsername, "quit") == 0){quitFlag = 1;pthread_mutex_unlock(&Umutex);return;}strncpy(UWmes->targetname, targetUsername, SIZE - 1);UWmes->messageType = 1;printf("请输入消息内容:");if (!fgets(UWmes->note, SIZE, stdin)){printf("输入错误\n");pthread_mutex_unlock(&Umutex);return;}UWmes->note[strcspn(UWmes->note, "\n")] = 0;int total = 0;int size = sizeof(MES);char *ptr = (char *)UWmes;while (total < size){int n = write(sockUserFd, ptr + total, size - total);if (n <= 0){perror("发送消息失败");break;}total += n;}pthread_mutex_unlock(&Umutex);
}void fileSend(MES *UWmes)
{pthread_mutex_lock(&Umutex);char targetUsername[SIZE];printf("请输入目标用户名:");if (!fgets(targetUsername, SIZE, stdin)){printf("输入错误\n");pthread_mutex_unlock(&Umutex);return;}targetUsername[strcspn(targetUsername, "\n")] = 0;strncpy(UWmes->targetname, targetUsername, SIZE - 1);UWmes->messageType = 2;printf("请输入文件路径:");char filePath[SIZE];if (!fgets(filePath, SIZE, stdin)){printf("输入错误\n");pthread_mutex_unlock(&Umutex);return;}filePath[strcspn(filePath, "\n")] = 0;int fd = open(filePath, O_RDONLY);if (fd < 0){perror("文件打开失败");pthread_mutex_unlock(&Umutex);return;}int fdSize = read(fd, UWmes->file.data, FDSIZE);if (fdSize < 0){perror("文件读取失败");close(fd);pthread_mutex_unlock(&Umutex);return;}UWmes->file.size = fdSize;strncpy(UWmes->file.name, filePath, SIZE - 1);int total = 0;int size = sizeof(MES);char *ptr = (char *)UWmes;while (total < size){int n = write(sockUserFd, ptr + total, size - total);if (n <= 0){perror("发送文件失败");break;}total += n;}close(fd);pthread_mutex_unlock(&Umutex);
}void cleanStdin()
{int ch;while ((ch = getchar()) != '\n' && ch != EOF);
}
一、整体结构

本项目是基于 TCP 的聊天系统,分为 服务端客户端 两部分,支持:

  • 用户私聊

  • 文件传输

  • 在线用户列表广播

  • 动态线程管理与回收

核心思想:

  1. 服务端监听端口,接收用户连接。

  2. 每个用户连接由独立线程处理(functionUser)。

  3. 使用链表维护在线用户信息。

  4. 通过 MES 结构体进行统一消息封装。


二、主要数据结构
1. 消息结构体 MES
typedef struct message {int messageType;       // 0服务器消息,1用户消息,2文件消息char targetname[32];   // 目标用户名char note[SIZE];       // 消息内容struct File file;      // 文件结构体
} MES;
  • messageType:区分普通消息、私聊消息、文件消息。

  • note:文本消息或提示。

  • file:包含文件名、内容和大小。

2. 客户端链表节点 CN
typedef struct ClientNode {pthread_t threadid;          // 用户线程IDint userfd;                  // 用户套接字struct sockaddr_in userAddr; // 用户地址char username[SIZE];         // 用户名struct ClientNode *next;     // 链表指针int isFinished;              // 线程退出标志
} CN;

作用:维护在线用户信息,支持查找、删除、广播。


三、服务端逻辑
1. 主流程(main
  1. 创建 sockServerFd,绑定 IP/端口并监听。

  2. 启动两个后台线程:

    • functionServer:接收新连接,分配线程处理用户。

    • functionMonitor:负责线程回收和链表清理。

  3. 主线程等待子线程退出。

2. functionServer
  • 使用 accept 接收新用户。

  • 读取用户名,创建用户线程 functionUser

  • 调用 Node 将用户加入链表。

  • 向该用户发送当前在线用户列表。

  • 向所有人广播“某用户上线”。

3. functionUser
  • 循环 read 用户发来的 MES 消息。

  • 判断消息类型:

    • 1:私聊消息 → 调用 transmitUserMessage 转发。

    • 2:文件消息 → 转发给目标用户。

  • 用户断开时:

    • 标记链表节点 isFinished=1

    • 广播“用户下线”。

4. functionMonitor
  • 定时扫描链表。

  • 如果某线程 isFinished=1pthread_join 并释放链表节点。


四、客户端逻辑
1. 主流程(main
  1. 创建 sockUserFd,连接服务器。

  2. 输入用户名并发送给服务器。

  3. 创建两个线程:

    • functionRead:接收服务器/用户消息并打印。

    • functionWrite:交互式输入,发送消息或文件。

2. functionRead
  • 循环读取 MES,根据类型输出:

    • 0:服务器消息

    • 1:私聊消息

    • 2:文件消息(带文件名与大小)

3. functionWrite
  • 菜单操作:

    • 1:调用 messageSend,发送文本消息。

    • 2:调用 fileSend,发送文件。

    • 3:退出,设置 quitFlag,关闭读端,让 functionRead 退出。


五、关键函数说明
  • Node:添加用户节点到链表。

  • deleteNode:删除指定线程对应的节点。

  • transmitUserMessage:根据目标用户名转发消息或文件。

  • sendOnlineUsers:将在线用户列表发送给新上线用户。

  • broadcastMessage:向所有用户广播消息。


六、注意事项
  1. 并发安全:链表和套接字操作均使用 pthread_mutex_t 加锁。

  2. 粘包问题:这里直接以 MES 结构体作为传输单元,避免了粘包问题。

  3. 文件传输限制:文件内容存储在 FDSIZE 数组,大小有限,适合小文件。

  4. 线程回收:通过 isFinished 标记 + functionMonitor 循环清理,避免僵尸线程。

http://www.dtcms.com/a/400706.html

相关文章:

  • 上海建设摩托车官方网站抖音seo公司
  • 红安建设局官方网站金华手机建站模板
  • 企业网站推广费用开发软件都有哪些
  • 织梦mip网站改造wordpress二次元简约主题
  • 企业网站怎么建微商城网站建设代理商
  • 网站建设工作人员有哪些职责大同工程建设信息网
  • 一家专门做原型的网站国建设网站
  • jsp做的婚恋网站如何让网站自适应手机
  • 陕西交通建设集团西商分公司网站北京星光灿烂影视有限公司
  • 网站上传ftp返利淘网站怎么做
  • 网站上传源码后怎么弄厦门网站推广步骤机构
  • 网站建设-易速通科技厦门某某公司网站
  • 公司网站建设制作商济南seo推广效果好
  • Linux文件fd-重定向-缓冲区
  • SpringAI Alibaba 集成与简单使用
  • 网站规划与建设规划书wordpress如何添加目录菜单
  • 常州市网站建设设计深圳国外网站制作公司
  • 万网建站流程网络规划设计师属于高级职称吗
  • wordpress建站小百科网站手机模板和pc模板要分开做
  • 网站设计建设公司1.2婚庆网站建设的目的
  • wordpress插件系统大连百度推广seo
  • 张家港做网站的公司用公司注册公司需要什么资料
  • 网站制作替我们购买域名wordpress docker镜像
  • 化肥厂的网站摸板wordpress修改登陆地址后缀
  • 网站设计制作价钱网站开发河南
  • jsp 网站开发永州做网站的公司
  • 广州哪里有做网站icp网站备案系统
  • 手机网站的作用asp装饰公司网站源码
  • 淘宝客网站虚拟主机什么是网络营销促销?网络营销促销有何作用?
  • 池州网站制作哪家好高端网名好听又有个性