基于TCP/IP协议的网络多人聊天室

项目背景

        基于 TCP/IP 的网络多人聊天室是一种旨在提供实时的文字交流平台,使多个用户能够在网络上进行即时聊天和互动的应用程序。它通过利用 TCP/IP 协议栈中的传输控制协议(TCP)和 Internet 协议(IP)来实现消息的传输和网络通信。

该网络多人聊天室是基于传输层TCP协议由服务端模块与客户端模块组成。服务器端负责接收和转发消息,而客户端负责与服务器建立连接并发送/接收消息。TCP协议是一种一对一连接协议,它在客户端和服务端之间建立可靠的通信连接,确保数据的可靠性和有序性。因此,客户端只能与服务端进行一对一连接通信,但可通过服务器与其他客户端完成一对多通信。

  • 服务端作为该网络聊天室的核心组件,负责接受来自客户端的连接请求,维护客户端的连接状态,并处理消息的转发。
  • 服务器端通常使用单线程或多线程的模型来处理客户端的连接和消息。每个客户端连接会被分配一个独立的线程,负责处理该客户端的消息收发。
  • 服务器端需要维护一个用户列表或用户数据库,用于管理在线用户和其相关信息,如用户名、IP 地址等。服务器端还负责处理系统通知、用户加入/退出聊天室等特殊事件的处理。

  • 客户端是用户使用的界面,允许用户输入消息并发送给服务器,同时接收其他用户发送的消息。
  • 客户端可以由线程组成,这种方式通常称为多线程编程。多线程编程是一种并发编程的方式,允许在一个程序中同时执行多个线程,每个线程都可以执行客户端要求的任务,并且多个线程可以共享一个进程的资源,可以提高系统资源利用率并节约内存空间。
  • 客户端需要与服务器建立连接,并通过套接字(Socket)与服务器进行通信。客户端通常提供用户注册、登录、选择聊天室、显示在线用户列表等功能。客户端还负责将用户输入的消息发送给服务器,并将接收到的消息显示在用户界面上。

 软件开发平台

  1. Ubuntu20.04操作系统
  2. 基于ssh网络远程连接Linux系统的VScode代码编辑器
  3. 开发语言:C语言

功能分析

服务器功能

  1. 登录界面

  2. 循环服务器。循环登录功能。
  3. 可以显示好友信息。好友名称 + 好友 ip + port
  4. 保存聊天信息 + 保存日志内容。//保存到文件中。或者是链表中。或者数据库中。
  5. 多用户登录功能。并发服务器。使用多线程技术,多进程技术。
  6. 群聊功能。转发功能。私聊功能。单独与好友聊天。
  7. 上线提醒功能。某人上线了,所有人都知道。服务器给所有人发送信息。
  8. 用户标识符功能:jack> mike: linkda= //字符串连接函数。

  9. 任务排斥,其他任务抢占现象:线程互斥锁 条件变量 信号量。进程文件锁,进程信号量。
  10. 信号功能。信号注册。signal sigaction

  11. 接收文件。//接受文件名 + 以这个名字命名一个文件 + 接受文件内容。

客户端功能

  1. 登录成功以后:欢迎信息。
  2. 信号注册功能。ctrl + c : 显示当前时间。
  3. 发送文件。
  4. 互斥锁。文件锁 信号量。
  5. 自定义功能。广告功能,QQ秀功能。群通知功能。匿名聊天功能。时间戳功能。踢人功能。敏感词屏蔽。

聊天室系统服务端、客户端模块运行流程图

  • 服务端运行流程图

  • 客户端运行流程图

客户端模块相关代码

  • 用户登录

//定义用户信息节点
typedef struct userdata{
    void *data;
    struct userdata *next;
}user_t;

//data指针 数据指向
typedef struct logdata{
    char username[16];
    char userpasswd[16];
    int user_cid;
}log_t;

log_t *login(char ip[],unsigned int port)
{
    FILE *fp = fopen("user_data.txt","a+");
    fseek(fp,0,SEEK_SET);

    log_t *logdata = (log_t *)malloc(sizeof(log_t *));
    // 1.存用户名
    printf("\tWelcome to the chat room\n\n");
    printf("username:");
    scanf("%s",logdata->username);
    fprintf(fp,"username:%s\n",logdata->username);

    // 2.存密码
    printf("password:");
    scanf("%s",logdata->userpasswd);
    fprintf(fp,"password:%s\n",logdata->userpasswd);

    // 存储用户信息 IP + Port    
    fprintf(fp,"IP:%s\n",ip);
    fprintf(fp,"Port:%d\n",port);
    
    // 关闭文件
    fclose(fp);
    
    return logdata;
}

void get_time(FILE * fp)
{
    time_t currentTime = time(NULL);
    struct tm *localTime = localtime(&currentTime);
    
    fprintf(fp,"time: %d-%02d-%02d %02d:%02d:%02d\n\n",
           localTime->tm_year + 1900,
           localTime->tm_mon + 1,
           localTime->tm_mday,
           localTime->tm_hour,
           localTime->tm_min,
           localTime->tm_sec);
}

void sigfun(int signo)
{
    // ctrl + c
    if(signo == SIGINT)
    {
        time_t currentTime = time(NULL);
        struct tm *localTime = localtime(&currentTime);
        printf("time: %d-%02d-%02d %02d:%02d:%02d\n\n",
           localTime->tm_year + 1900,
           localTime->tm_mon + 1,
           localTime->tm_mday,
           localTime->tm_hour,
           localTime->tm_min,
           localTime->tm_sec);
    }
}
  • 具体实现

  • #include "cJSON.h"
    
    int file_pass_flag = 0;
    
    void file_pass(int signo)
    {
        // ctrl + z
        if(signo == SIGTSTP)
        {
            // printf("file_pass_flag = %d\n",file_pass_flag);
            // file_pass_flag = 21;
            // printf("file_pass_flag = %d\n",file_pass_flag);
            system("sl");
        }
    }
    
    int main(int argc, char const *argv[])
    {
        if(argc < 5)
        {
            perror("usage:./client + s_IP + Port + c_IP + Port\n");
            return -1;
        }
        
        // !-------------select准备部分------------------
        // 输入文件描述符集合
        fd_set rset = {0};
        // 设定超时时间
        struct timeval tm = {0};
        // 有数据变化的文件描述符个数
        int count  = 0;
        char bufh[128] = "";
        // !---------------------------------------------
    
        // 已经登录
        char tcname[16] = "";
        // strcpy(tcname,login((char *)argv[1],atoi(argv[2])));
        
        log_t *users_data = login((char *)argv[1],atoi(argv[2]));
        if (strcmp(users_data->username,""))
        {
            printf("\nusersname:%s---欢迎\n",users_data->username);
            signal(SIGINT,sigfun);
        }   
        
        strcpy(tcname,users_data->username);
        //建立客户端套接字
        int cid = socket(AF_INET,SOCK_STREAM,0);
        printf("cid = %d\n",cid);
    
        //connect
        struct sockaddr_in caddr = {0};
        caddr.sin_family = AF_INET;
        caddr.sin_port = htons(atoi(argv[2]));
        caddr.sin_addr.s_addr = inet_addr(argv[1]);
        if(connect(cid,(struct sockaddr*)&caddr,sizeof(caddr)) < 0 )
        {
            perror("connect error\n");
            return -3;
        }
        printf("connect successful\n");
        // !看看是否成功接收到客户端登录name
        printf("---%s---\n",tcname);
        
        // !在这里初始化一个接收服务器信息的地址
        char rbuf[512] = "";
        char bufv[128] = "";
        struct sockaddr_in rAddr = {0};
        rAddr.sin_family = AF_INET;
        rAddr.sin_addr.s_addr = inet_addr(argv[3]);
        rAddr.sin_port = htons(atoi(argv[4]));
    
        // 接收条件
        cJSON *json_res = NULL;
        cJSON *json_name = NULL;
        cJSON *json_code = NULL;
        cJSON *json_info = NULL;
        cJSON *json_sign = NULL;
        cJSON *fuhao = NULL;
        cJSON *json_ip = NULL;
        cJSON *json_port = NULL;
        // !私聊对象
        cJSON *json_chat_name = NULL;
    
        // 先发送自己的客户端用户名给服务器
        send(cid,(char *)tcname,sizeof(tcname),0);
        
        char bufs[128] = "";
        int len = 0 ;
        
        //------------------json的数据封装。------------------------------//
        cJSON * json_pointer = NULL;
        //创建一个链表数据对象。
        json_pointer = cJSON_CreateObject();
        //1添加字符串类型到节点当中 姓名
        char cname[16] = "";
        
        cJSON_AddStringToObject(json_pointer,"name",tcname);
        //2 添加整型数据 密码
        char cpasswd[16] = "1221";
        // 匹配密码
        FILE *cfp  = fopen("user_data.txt","r+");
        // fscanf(cfp,"password:%s\n",cpasswd);
        
        cJSON_AddStringToObject(json_pointer,"code",cpasswd);
        //3 添加字符串类型到节点当中 信息
        //cJSON_AddStringToObject(json_pointer,"info","hello linux");
        //4 添加字符串类型到节点当中 签名
        cJSON_AddStringToObject(json_pointer,"sign","Fear of violence");
        //添加字符串类型到节点当中 
        cJSON_AddStringToObject(json_pointer,"fuhao",">");
        // 添加IP字符串到节点中
        char cIP[16] = "";
        strcpy(cIP,argv[3]);
        printf("cIP = %s\n",cIP);
        cJSON_AddStringToObject(json_pointer,"IP",cIP);
        // 添加端口号到节点中
        unsigned int cPort = htons(atoi(argv[4]));
        cJSON_AddNumberToObject(json_pointer,"Port",cPort);
        
        //数据整理
        char * str = NULL;
        char chat_name[16] = "";
    
        //循环读写
        while(1)
        {
            bzero(chat_name,sizeof(chat_name));
            bzero(bufs,sizeof(bufs));
            bzero(bufv,sizeof(bufv));
    
            // !select循环读写设置部分
            FD_SET(cid,&rset);
            FD_SET(STDIN_FILENO,&rset);
            tm.tv_sec = 2;
            count = select(cid+1,&rset,NULL,NULL,&tm);
    
            signal(SIGTSTP,file_pass);
            // 键盘有动作,说明客户端要发数据
            if (FD_ISSET(STDIN_FILENO, &rset))  
            {
                // 选择聊天对象
                read(STDIN_FILENO, chat_name, sizeof(chat_name) - 1);
                if (file_pass_flag == 0)
                {
                    // 从键盘读数据再写给对面
                    read(STDIN_FILENO, bufs, sizeof(bufs) - 1);
    
                    // 增加私聊功能
                    printf("chat:%s",chat_name);
                    cJSON_AddStringToObject(json_pointer, "chat_name", chat_name);
                    // 聊天信息部分
                    cJSON_AddStringToObject(json_pointer, "info", bufs);
    
                    // jason格式转换成字符串格式
                    str = cJSON_Print(json_pointer);
                    FILE * fp = fopen("chat_logs.txt","a+");
                    fprintf(fp,"%s-->%sinfo:%s",tcname,chat_name,bufs);
                    get_time(fp);
                    send(cid, str, strlen(str), 0);
                    // 发送过去之后,进行删除节点。
                    if (!strncmp(bufs, "quit", 4))
                    {
                        cJSON_DeleteItemFromObject(json_pointer, "info");
                        break;
                    }
                    cJSON_DeleteItemFromObject(json_pointer, "info");
                    cJSON_DeleteItemFromObject(json_pointer,"chat_name");
                    printf("提示:客户端发送信息时,先输入聊天对象,再输入聊天内容\n");
                    fclose(fp);    
                }
                if (file_pass_flag == 21)
                {
                    char filename[16] = "";
                    printf("输入一个文件名:");
                    scanf("%s",filename);
                    strncpy(filename,filename,strlen(filename)-1);
                    FILE * fp = fopen(filename,"w+");
                    printf("请输入文件内容:");
                    // 清空键盘缓存区域
                    fflush(stdin);
                    // fputs();
                }
                signal(SIGTSTP,SIG_DFL);            
            }
    
            if (FD_ISSET(cid,&rset))
            {
                printf("服务器正在发送信息~\n");
                bzero(rbuf, sizeof(rbuf));
                // 接收服务器发出数据
                len = recv(cid, rbuf, sizeof(rbuf), 0);
    
                // 解析rbuf
                json_res = cJSON_Parse(rbuf);
                // 姓名
                json_name = cJSON_GetObjectItem(json_res, "name");
                // 密码
                json_code = cJSON_GetObjectItem(json_res, "code");
                // 信息
                json_info = cJSON_GetObjectItem(json_res, "info");
                // 签名
                json_sign = cJSON_GetObjectItem(json_res, "sign");
                // 符号
                fuhao = cJSON_GetObjectItem(json_res, "fuhao");
                // IP
                json_ip = cJSON_GetObjectItem(json_res, "IP");
                // Port
                json_port = cJSON_GetObjectItem(json_res, "Port");
                // !聊天对象
                json_chat_name = cJSON_GetObjectItem(json_res, "chat_name");
    
                printf("来自:%s的信息_:%s",json_name->valuestring,json_info->valuestring);
                printf("%s %s的个性签名:%s\n",fuhao->valuestring,json_name->valuestring,json_sign->valuestring);
                printf("IP:%s\tPort:%d\n",json_ip->valuestring,json_port->valueint);    
            }
            
        }
        shutdown(cid,SHUT_RDWR);
        fclose(cfp);
    
        return 0;
    }
    

     服务端模块相关代码

  • 功能实现

#include "cJSON.h"

//静态 cid ; 
char bufs[512] = "";
char bufv[128] = "";
int len = 0 ;

// 建立链表,在服务器中建立链表,方便使用
user_t *head = NULL;

void * pthread_fun(void * arg)
{
    //cid是从主线程传过来的。   
    int pcid = *(int *)arg;

    // !-------------select准备部分------------------
    // 输入文件描述符集合
    fd_set rset = {0};
    // 设定超时时间
    struct timeval tm = {0};
    // 有数据变化的文件描述符个数
    int count  = 0;
    char bufh[128] = "";
    // !---------------------------------------------

    // 已经在主线程中创建链表了并使用头插法插入新的客户端链表
    // !------------为了通过name转发而做准备,准备一个单链表--------------
    user_t *temp = head->next;
    char sname[16] = "";
    bzero(sname,sizeof(sname));
    recv(pcid,(char *)sname,sizeof(sname),0);
    while(temp!=NULL)
    {
        if (((log_t *)(temp->data))->user_cid == pcid)
        {
            memcpy(((char *)(((log_t *)(temp->data))->username)),sname,16);
            printf("%s is online\n",(char *)(((log_t *)(temp->data))->username));
            break;
        }
        temp = temp->next;
    }
    // !-----------------------------------------------------------------

    //清空bufs
    bzero(bufs,sizeof(bufs));
    cJSON * json_res = NULL;
    cJSON * json_name = NULL;
    cJSON * json_code = NULL;
    cJSON * json_info = NULL;
    cJSON * json_sign = NULL;
    cJSON * fuhao = NULL;
    cJSON * json_ip = NULL;
    cJSON * json_port = NULL;
    // !私聊对象
    cJSON * json_chat_name = NULL;

    while(1)
    {
        // !select循环读写设置部分
        FD_SET(pcid,&rset);
        FD_SET(STDIN_FILENO,&rset);
        tm.tv_sec = 10;
        count = select(pcid+1,&rset,NULL,NULL,&tm);

        //cid 要传递过来
        bzero(bufs,sizeof(bufs));
        // !接收
        len = recv(pcid,bufs,sizeof(bufs),0);
        //解析bufs
        json_res = cJSON_Parse(bufs);
        // 姓名
        json_name = cJSON_GetObjectItem(json_res,"name");
        // 密码
        json_code = cJSON_GetObjectItem(json_res,"code");
        // 信息
        json_info = cJSON_GetObjectItem(json_res,"info");
        // 签名
        json_sign = cJSON_GetObjectItem(json_res,"sign");
        // 符号
        fuhao = cJSON_GetObjectItem(json_res,"fuhao");
        // IP
        json_ip = cJSON_GetObjectItem(json_res,"IP");
        // Port
        json_port = cJSON_GetObjectItem(json_res,"Port");
        // !聊天对象
        json_chat_name = cJSON_GetObjectItem(json_res,"chat_name");     

        temp = head->next;
        int send_num = 1;
        int send_jud = 0;
        while (temp!=NULL)
        {
            // 判断是否私发给服务器
            if (!strncmp(json_chat_name->valuestring,"server",6)&&send_num--)
            {
                printf("来自:%s的信息_:%s",json_name->valuestring,json_info->valuestring);
                printf("%s %s的个性签名:%s\n",fuhao->valuestring,json_name->valuestring,json_sign->valuestring);
                printf("IP:%s\tPort:%d\n",json_ip->valuestring,json_port->valueint);
                printf("chat_name:%s",json_chat_name->valuestring);
                send_num = 0;
                send_jud = 1;
            }
            // 通过服务器转发给其他用户 --- 比较name
            if (!strncmp(json_chat_name->valuestring,((log_t *)temp->data)->username,strlen(json_chat_name->valuestring)-1))
            {
                // 经过测试,确实存在转发,并且转发的字节数是对的
                send_jud = send(((log_t *)temp->data)->user_cid,bufs,strlen(bufs),0);
            }

            // 群发
            if (!strncmp(json_chat_name->valuestring,"all",3))
            {
                user_t *temp_all = head->next;
                while(temp_all!=NULL)
                {
                    if (strncmp(((log_t *)(temp_all->data))->username,json_name->valuestring,strlen(json_name->valuestring)))
                    {
                        send_jud = send(((log_t *)temp->data)->user_cid,bufs,strlen(bufs),0);    
                    }                   
                    temp_all = temp_all->next;
                }
                if (send_num)
                {
                    // 服务器显示的部分
                    printf("来自:%s的信息_:%s",json_name->valuestring,json_info->valuestring);
                    printf("%s %s的个性签名:%s\n",fuhao->valuestring,json_name->valuestring,json_sign->valuestring);
                    printf("IP:%s\tPort:%d\n",json_ip->valuestring,json_port->valueint); 
                    send_num = 0;   
                }                
            }
            temp = temp->next;
        }
        if(!send_jud)
        {
            printf("不存在该用户:%s",json_chat_name->valuestring);
        }
        send_jud = 0;
        if (!strncmp(json_info->valuestring,"quit",4))
        {
            printf("来自IP:%s的客户端正在退出~\n",json_ip->valuestring);
            break;
        }        
        temp = head->next;
    }
    //线程退出 - cid关闭是不是一件事?
    //shutdown(pcid,SHUT_RDWR);
    close(pcid);
    // ! pcid = -1;
    delete_vlinklist(head,pcid); 

    printf("来自IP:%s的客户端退出完毕~\n",json_ip->valuestring);
    //线程退出
    pthread_exit((void *)0);
}

//多用户登录 - 并发服务器 - 线程方法
int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        perror("usage:./server + ip + port\n");
        return -1;
    }
    head = create_linklist();

    //.建立套接字
    int sid = socket(AF_INET,SOCK_STREAM,0);
    printf("sid = %d\n",sid);

    //实际地址结构体
    struct sockaddr_in saddr = {0};
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = inet_addr(argv[1]);    
    saddr.sin_port = htons(atoi(argv[2]));
    if(bind(sid,(struct sockaddr*)&saddr,sizeof(saddr))<0)
    {
        perror("bind error\n");
        return -2;
    }
    printf("bind successful\n");

    //3.监听
    listen(sid,10);

    //4.循环连接
    char bufs[128] = "";
    int len = 0 ;
    pthread_t tid = 0 ;
    //只要初始化一次,在全局数据区
    static int cid = -1;
    while (1)
    {   
        //主线程。  链接。
        cid = accept(sid,NULL,NULL);
        if(cid != -1)
        {   
            // !这段说明与建立客户端连接成功
            log_t *log_data = (log_t*)malloc(sizeof(log_t));
            log_data->user_cid = cid;
            head = insert_hlinklist(head,log_data);

            //把线程设定成分离属性 - 线程执行完毕以后,自动释放占用的空间。
            pthread_create(&tid,NULL,pthread_fun,&cid);
            pthread_detach(tid);
            printf("有新用户登录 cid = %d\n",cid);
        }
        
    }
    // 销毁链表
    destroy_linklist(&head);

    close(sid);

    return 0;
}
  • 服务端应用数据结构
    //1.创建一个单链表
    user_t * create_linklist()
    {
    	user_t * head =(user_t *)malloc(sizeof(user_t));
    	if(head ==NULL)
    	{
    		perror("create error\n");
    		return NULL;
    	}
        head->data = NULL;
    	head->next = NULL;
        return head;	
    }
    
    //2.头插法插入结点
    user_t * insert_hlinklist(user_t * head, log_t * log_data)
    {
    	//数据判断
    	if(log_data == NULL||head==NULL)
    	{
    		perror("parameter error !\n");
    		return (user_t *)-1;
    	}
    	//申请空间
    	user_t * newnode = (user_t *)malloc(sizeof(user_t));
    	if(newnode==NULL)
    	{
    		perror("newnode create error!\n");
    		return (user_t *)-2;
    	}
    
    	newnode->data = (log_t *)malloc(sizeof(log_t));
    	memcpy(((log_t *)(newnode->data)),log_data,sizeof(log_t));    
    
    	newnode->next = head->next;
        head->next = newnode;
    
    	return head;
    }
    
    //根据部分信息输出全部信息
    typedef int (*cmpfun_t)(void *data1,void *data2);
    //1.比对函数
    int cmpname(void *data1,void *data2)
    {
    	log_t* newdata1 = (log_t*)data1;
    	log_t* newdata2 = (log_t*)data2;
    	return strcmp(newdata1->username,newdata2->username);
    }
    
    user_t * Search_linklist(user_t * head,void *value,cmpfun_t cmpfun)
    {
    	//1.参数判断
    	if(head == NULL || head->next == NULL || value == NULL || cmpfun == NULL)
    	{
    		perror("ERROR!\n");
    		return (user_t*)-1;
    	}
    	//2.
    	user_t * temp = head->next;
    	while (temp != NULL)
    	{
    		if (!cmpfun(temp->data,value))
    		{
    			return temp;
    		}
    		temp = temp->next;
    	}
    	return NULL;
    }
    
    // 按cid查找并且删除 节点
    user_t * delete_vlinklist(user_t*head,unsigned int cid)
    {
        //1.参数判断
        if (head == NULL || head->next==NULL)
        {
            /* 用perror也可以*/
            printf("head is null or linklist is illegal\n");
            return (user_t *)-1;
        }
        //2.遍历
        user_t*temp = head->next,*before = head;
        while (temp != NULL &&(((log_t *)(temp->data))->user_cid!=cid))
        {
            //before先移动
            before = temp;
            //temp再移动
            temp = temp->next;
        }
        //出界判断
        if (temp == NULL)
        {
            perror("this value is not in this linklist\n");
            return (user_t *)-1;
        }
        //3.修改指针
        before->next  = temp->next;
        free(temp);
        temp = NULL;
        return head;
    }
    
    //删除所有结点的数据并销毁链表
    int destroy_linklist(user_t **head)
    {
        if(*head == NULL)
        {
            perror("linklist is not exits\n");
            return -1;
        }
        //先取出元素 再取出成员
        user_t*temp =(*head)->next ,*before = NULL;  
        (*head) ->next = NULL;
        while( temp != NULL)
        {
            before = temp;
            temp = temp->next;
            free(before);
        }
        free(*head);
        *head = NULL;
        return 0 ;
    }

            在该项目的信息传输过程中应用了CJSON,用于解析JSON数据、构建JSON对象和数组,以及将JSON数据列化为字符串,使数据在网络传输后能够方便地被另一端解析和处理。

心得体会

  1.  这个项目让我有机会亲自实践多线程编程方法,并且我在这个过程中取得了一些实践成果。学会了如何在两个不同的主机之间进行通信,学习到了如何利用不同的信号功能来提高代码的可读性。
  2. 在网络编程方面,我学会了使用套接字(socket)进行网络连接,发送和接收数据。这让我对网络通信的原理有了更深入的理解,并且能够编写简单的网络应用程序来实现数据交换。
  3. 我学习到了如何使用信号来增强代码的可读性。通过合理地使用信号,我可以在代码中引入清晰的逻辑和结构。例如,我可以使用信号来处理异常情况或处理特定的事件等。
  • 52
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
<计算机网络实验> 基于TCP网络聊天室的设计 -实验指导 一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值