基于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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值