第二十五、二十六天:基于UDP的网路聊天程序

    连续四天学习套接字的编程,可见套接字的重要性了。基于TCP和UDP分别写了两个程序。一是利用TCP实现一个服务器对多个客户端,客户端你发送信息,服务器就从事先准备好的五个字符串中随机回复一条。另一个是利用UDP实现两个人的对话,对话时可以是多个信息同时输入。

   先是第一个程序。要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过accept函数接受并创建一个线程来处理。线程的创建函数为int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);这个函数是一个回调函数,会调用start_routine函数指针指向的函数进行处理,后面的arg为指向函数的参数。线程还有个重要的函数是 int pthread_join(pthread_t thread, void **retval);用来等待一个线程的结束。retval用来存放线程的返回值。下面是这个程序的源代码:   

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<sys/socket.h>
 5 #include<netinet/in.h>
 6
 7 void *func(void *arg){
 8
 9     int ret = 0;
10     int nfd = (int)arg;
11     char revdata[1024] = {0};
12     char *talk[5] = {0};
13     *(talk + 0) = "hello world !";
14     *(talk + 1) = "hello bunfly !";
15     *(talk + 2) = "hello good  !";
16     *(talk + 3) = "hello xixi !";
17     *(talk + 4) = "hello lala !";
18     int i = 0;
19     while(1){
20         ret = recv(nfd,revdata,1024,0);
21         if(ret < 0){
22             perror("recv");
23             exit(EXIT_FAILURE);
24         }
25         ret = send(nfd,*(talk +i),strlen(*(talk+i)),0);
26         if(ret < 0){
27             perror("recv");
28             exit(EXIT_FAILURE);
29         }
30         printf("ret is %d\n",ret);
31         i = rand() % 4;
32     }
33     close(nfd);
34 }
35 int main()
36 {
37     /*创建套接口*/
38     int  fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
39     if(fd < 0){
40         perror("socket");
41         exit(EXIT_FAILURE);
42     }
43     /*服务端信息*/
44     struct sockaddr_in srv;
45     srv.sin_family = AF_INET;
46     srv.sin_port=htons(9527);
47     srv.sin_addr.s_addr = inet_addr("192.168.31.200");
48     /*绑定*/
49     int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
50     if(ret < 0){
51         perror("bind");
52         exit(EXIT_FAILURE);
53     }
54     /*监听*/
55     ret = listen(fd,10);
56     if(ret < 0){
57         perror("bind");
58         exit(EXIT_FAILURE);
59     }
60     /*接受*/
61     struct sockaddr_in snd;
62     int snd_len = 0;
63     pthread_t pid;
64     while(1){
65         int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
66         if(nfd < 0){
67             perror("accpet");
68             exit(EXIT_FAILURE);
69         }
70         ret = pthread_create(&pid,NULL,func,(void *)nfd);
71         if(ret < 0){
72             perror("pthread_create");
73             return 1;
74         }
75     }
76     close(fd);
77 }

服务端

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<sys/socket.h>
 5 #include<netinet/in.h>
 6 #include<fcntl.h>
 7
 8 int main()
 9 {
10     /*创建套接口*/
11     int  fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
12     if(fd < 0){
13         perror("socket");
14         exit(EXIT_FAILURE);
15     }
16     /*服务端信息*/
17     struct sockaddr_in srv;
18     srv.sin_family = AF_INET;
19     srv.sin_port=htons(9527);
20     srv.sin_addr.s_addr = inet_addr("192.168.31.122");
21     /*链接*/
22     int ret = connect(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
23     if(ret < 0){
24         perror("connect");
25         exit(EXIT_FAILURE);
26     }
27     /*聊天*/
28     char data[1024] = {0};
29     char revdata[1024] = {0};
30     int i = 0;
31     while(1){
32         ret = read(0,data,1024);
33         if(ret < 0){
34             perror("read");
35             exit(EXIT_FAILURE);
36         }
37         ret = send(fd,data,1024,0);
38         if(ret < 0){
39             perror("send");
40             exit(EXIT_FAILURE);
41         }
42         ret = recv(fd,revdata,1024,0);
43         if(ret < 0){
44             perror("send");
45             exit(EXIT_FAILURE);
46         }
47         printf("service say: %s\n",revdata);
48     }
49     close(fd);
50
51 }

客户端

  上面的代码如果在实际的操作中有着明显的漏洞。监听程序最大允许接受10个连接请求,如果这十个一直连接不断开的话,后续的连接请求就无法得到处理。正确的方式应该是着几个请求在完成一个连接,执行一个操作后又要重新连接。

   TCP可靠的连接方式造成了连接过错比较复杂。消耗资源,所以大部分程序就是使用UDP的方式,比如迅雷,QQ。

   使用UDP聊天,这就意味着双方都要进行绑定操作。前面写的程序当发送信息后,程序就处于等待接收的状态。一直等到有信息来了之后才能够继续发送信息。这种状态称为阻塞状态。这种方式明显是不可取的。这时就要利用int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout)函数实现I/O多路复用。具体的实现过程我在代码中有注释。

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/ioctl.h>
#include<net/if.h>

unsigned long getip(char *net_name);//获得指定网卡的ip。

int main(void)
{
    int fd  = 0;
    fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);//创建套接字,使用ipv4协议,数据报格式,UDP协议
    if(fd < 0){
        perror("socket");
        exit(EXIT_FAILURE);
    }
    struct sockaddr_in gg ;
    gg.sin_family = AF_INET;
    gg.sin_port = htons(9527);
    gg.sin_addr.s_addr = getip("eth0");//获取eth0网卡的ip地址

    int ret = bind(fd,(struct sockaddr *)&gg,sizeof(struct sockaddr));//向系统绑定gg的信息
    if(ret < 0){
        perror("bind");
        exit(EXIT_FAILURE);
    }
    fd_set rfds;
    char data[1024] = {0};
    char revdata[1024] = {0};
    struct sockaddr_in mm ;
    mm.sin_family = AF_INET;
    mm.sin_port = htons(9527);
    mm.sin_addr.s_addr = getip("eth1");//获得eth1网卡的ip地址
    int mm_len;
    while(1){
        memset(revdata,0,1024);
        memset(data,0,1024);//初始化数据,
        FD_ZERO(&rfds);
        FD_SET(0,&rfds);
        FD_SET(fd,&rfds);
        ret = select(fd+1,&rfds,NULL,NULL,NULL);//多路复用IO,使得fd,0处于非阻塞状态
        if(FD_ISSET(fd,&rfds)){//监听到fd有数据读入,执行数据接收
            ret = recvfrom(fd,revdata,1024,0,(struct sockaddr *)&mm,&mm_len);
            if(ret < 0){
                perror("recv");
                exit(EXIT_FAILURE);
            }
            printf("mm say: %s",revdata);
        }
        if(FD_ISSET(0,&rfds)){//监听到键盘有数据读入,执行数据的读取和发送
            ret = read(0,data,1024);
            if(ret < 0){
                perror("read");
                exit(EXIT_FAILURE);
            }
            ret = sendto(fd,data,ret,0,(struct sockaddr *)&mm,sizeof(struct sockaddr));
            if(ret < 0){
                perror("recv");
                exit(EXIT_FAILURE);
            }
        }
    }
    close(fd);
}
unsigned long getip(char *net_name){
    int sock;
    struct sockaddr_in sin;
    struct ifreq ifr;
    sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0){
        perror("socket");
        return -1;
    }
    strcpy(ifr.ifr_name,net_name);
    int ret = ioctl(sock,SIOCGIFADDR,&ifr);
    if(ret < 0){
        perror("ioctl");
        return -1;
    }
    memcpy(&sin,&ifr.ifr_addr,sizeof(sin));
    return inet_addr(inet_ntoa(sin.sin_addr));
}

gg方

 1 #include<unistd.h>
 2 #include<stdlib.h>
 3 #include<stdio.h>
 4 #include<string.h>
 5 #include<netinet/in.h>
 6 #include<sys/socket.h>
 7 #include<sys/ioctl.h>
 8 #include<net/if.h>
 9 unsigned long getip(char *net_name);
10
11 int main(void)
12 {
13     int fd  = 0;
14     fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
15     if(fd < 0){
16         perror("socket");
17         exit(EXIT_FAILURE);
18     }
19     struct sockaddr_in gg ;
20     gg.sin_family = AF_INET;
21     gg.sin_port = htons(9527);
22     gg.sin_addr.s_addr = getip("eth0");
23
24     fd_set rfds;
25     char data[1024] = {0};
26     char revdata[1024] = {0};
27     struct sockaddr_in mm ;
28     mm.sin_family = AF_INET;
29     mm.sin_port = htons(9527);
30     mm.sin_addr.s_addr = getip("eth1");
31     int ret = bind(fd,(struct sockaddr *)&mm,sizeof(struct sockaddr));
32     if(ret < 0){
33         perror("bind");
34         exit(EXIT_FAILURE);
35     }
36     int gg_len;
37     while(1){
38         memset(revdata,0,1024);
39         memset(data,0,1024);
40         FD_ZERO(&rfds);
41         FD_SET(0,&rfds);
42         FD_SET(fd,&rfds);
43         ret = select(fd+1,&rfds,NULL,NULL,NULL);
44         if(FD_ISSET(fd,&rfds)){
45             ret = recvfrom(fd,revdata,1024,0,(struct sockaddr *)&gg,&gg_len);
46             if(ret < 0){
47                 perror("recv");
48                 exit(EXIT_FAILURE);
49             }
50             printf("gg say: %s",revdata);
51         }
52         if(FD_ISSET(0,&rfds)){
53             ret = read(0,data,1024);
54             if(ret < 0){
55                 perror("read");
56                 exit(EXIT_FAILURE);
57             }
58             ret = sendto(fd,data,ret,0,(struct sockaddr *)&gg,sizeof(struct sockaddr));
59             if(ret < 0){
60                 perror("recv");
61                 exit(EXIT_FAILURE);
62             }
63         }
64     }
65     close(fd);
66 }
67 unsigned long getip(char *net_name){
68     int sock;
69     struct sockaddr_in sin;
70     struct ifreq ifr;
71     sock = socket(AF_INET,SOCK_DGRAM,0);
72     if(sock < 0){
73         perror("socket");
74         return -1;
75     }
76     strcpy(ifr.ifr_name,net_name);
77     int ret = ioctl(sock,SIOCGIFADDR,&ifr);
78     if(ret < 0){
79         perror("ioctl");
80         return -1;
81     }
82     memcpy(&sin,&ifr.ifr_addr,sizeof(sin));
83     return inet_addr(inet_ntoa(sin.sin_addr));
84 }

mm方

  两方的程序代码差不多。就是发送方和接收方对调。这个程序是实现本机的两个网卡eth0和eth1进行通话。利用getip函数获得指定网卡的ip.

  

   

时间: 2025-01-04 06:00:36

第二十五、二十六天:基于UDP的网路聊天程序的相关文章

JAVA实现UDP组播聊天程序

分类: Java 一.实验环境 编程语言:Java1.5(运行在JVM(Java Virsual Machine)) 开发工具:eclipce3.2 测试环境:局域网 二.实验目的 社会已经进入信息时代,网络技术在飞速发展.大量应用都依赖于从一个主机向多个主机或者从多个主机向多个主机发送同一信息的能力,在Internet上分布的数目可能达数十万台,这些都需要更高的带宽,并且大大超出了单播的能力.一种能最大限度地利用现有带宽的重要技术是IP组播. 三.组播聊天程序的实现: /** * 该程序实现了

CSS3秘笈复习:十三章&amp;十四章&amp;十五章&amp;十六章&amp;十七章

第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮动元素.关键字:left.right或both. (2).浮动外围元素:让包含浮动元素的<div>也浮动.选择这种方法一定要在浮动容器后面的任何元素中添加一个clear属性,确保浮动元素落到容器的下方. (3).利用overflow : hidden.另一种常见的方法是在外围的样式中添加以下属性:

基于UDP协议的网络程序

下图是基于UDP协议的客户端/服务器程序的一般流程: 图1.1 UDP协议通信流程 UDP套接口是无连接的.不可靠的数据报协议: 既然他不可靠为什么还要用呢?其一:当应用程序使用广播或多播时只能使用UDP协议:其二:由于他是无连接的,所以速度快.因为UDP套接口是无连接的,如果一方的数据报丢失,那另一方将无限等待,解决办法是设置一个超时. 建立UDP套接口时socket函数的第二个参数应该是SOCK_DGRAM,说明是建立一个UDP套接口:由于UDP是无连接的,所以服务器端并不需要listen或

基于UDP的MFC聊天程序设计

利用MFC创建基于UDP的聊天通信工具很简单,程序是基于MFC的对话框实现的.程序界面如下面所示: 1 概述 要添加的内容主要主要是发送端和接受端程序,以及最开始对话框程序初始化的时候对套接字的初始化.以及自定义消息WM_RECVDATA 2 接受线程 其次要注意的是因为接受函数recvfrom是一个阻塞函数,所以要开辟一个线程来专门接受消息.并且要把socket以及窗口句柄hwnd传递给线程的启动函数. 主要代码如下所示: BOOLCmfcChatDlg::OnInitDialog() { /

15.基于UDP协议的聊天室程序

使用UDP协议完成一个聊天室程序的小项目,大部分代码都有注释,一看就能看到的. 实现的功能:               (1)查看/显示已经登陆的用户信息               (2)向已登陆的用户发送消息               (3)输出错误消息,给予提示               (4)退出 共有三个文件: chat_public.h #ifndef _CHAT_PUB_H_ #define _CHAT_PUB_H_ //chat_public.h #include <lis

MYSQL必知必会读书笔记 第十五和十六章 联结表

为什么要使用联结? 如果数据存储在多个表中,怎样使用单条SELECT语句检索出数据?答案就是使用联结.简单地说,可以联结多个表返回一组输出,联结在运行时关联表中正确的行. 1.创建联结 SELECT vend_name,prod_name,prod_price from vendors,products WHERE vendors.vend_id=products.vend_id ORDER BY vend_name,prod_name; 注意:在引用列可能出现二义性时,必须使用完全限定列名.

基于udp的局域网聊天室

需要实现功能如下: 1.服务器端功能如下: 广播通知客服端登录/退出 接受客服端发来的消息,广播给所有用户 广播系统消息 2.客服端功能如下: 接收服务器发来的消息并显示 发送消息给服务器端 实现代码如下: //***********服务端******************* 1 #include <head.h> 2 3 #define CLIENT_LOGIN 10 4 #define CLIENT_QUIT 20 5 #define CLIENT_TALK 30 6 #define S

C++开发的基于UDP协议的聊天工具

项目相关地址 源码:https://github.com/easonjim/UDPChat bug提交:https://github.com/easonjim/UDPChat/issues

基于UDP协议的聊天室(java实现)

主要思路很简单: 1.设置自己的接收端口 2.设置对方IP和端口 3.发送数据和接收数据 下面是主要代码: import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; public class QQFrame extends JFrame implements Runnable { private JTextArea sendArea = new JT