C++实现局域网双向通信(socket)

来源:互联网 发布:大数据高并发 面试题 编辑:程序博客网 时间:2024/06/06 01:16
上一篇实现C++本地通信,当然这种数据传输的方式只能局限于个人主机,如果想要实现两不同局域网之间的主机进程通信,即实现局域网内两个电脑之间的数据传输,那么就不能通过管道或者命名管道的方式来实现,这里的局域网通信是通过一种叫做socket套接字的方式来实现。
首先想要与同一局域网内部的另一个电脑进行数据传输,需要知道它的IP地址,因为IP是连接网络之后分配给个人主机的唯一标识,所以先找到想要建立连接的主机的地址,然后我们双方共同约定一个类似于管道的唯一出入口(也就是端口号port),这里的端口号一般分为公认端口号和注册端口号,约定好端口号传输的时候双方都需要遵守共同的网络协议(TCP(SOCK_STREAM),UDP(SOCK_DRAM),ICMP(SOCK_RAW))。
公认端口号:知名端口即众所周知的端口号,范围从0到1023,这些端口号一般固定分配给一些服务。比如21端口分配给FTP(文件传输协议)服务,25端口分配给SMTP(简单邮件传输协议)服务,80端口分配给HTTP服务,135端口分配给RPC(远程过程调用)服务等等
注册端口号:端口号从1025到49151。它们松散地绑定于一些服务。也是说有许多服务绑定于这些端口,这些端口同样用于许多其他目的。这些端口多数没有明确的定义服务对象,不同程序可根据实际需要自己定义,如后面要介绍的远程控制软件和木马程序中都会有这些端口的定义的。记住这些常见的程序端口在木马程序的防护和查杀上是非常有必要的。常见木马所使用的端口在后面将有详细的列表
一般选择端口号都是选择注册端口号,没有明确的定义 服务对象,不同的通信可以根据自己的需要进行定义。

实现过程如下:
server:
#include <arpa/inet.h>//包含socket函数使用的各种协议族,send(),recv()#include <unistd.h>//调用linux系统函数的头文件(read(),write(),send(),select())#include <iostream>#include <thread>#include <list>#define PORT 7000#define IP "127.0.0.1"int s;struct sockaddr_in servaddr;socklen_t len;std::list<int> li;void getConn() {    while(1){        int conn = accept(s, (struct sockaddr*)&servaddr, &len);//第二个参数保存客户端套接字对应的IP地址和port 端口信息        li.push_back(conn);        printf("%d\n", conn);    }}void getData() {    struct timeval tv;    tv.tv_sec = 2;    tv.tv_usec = 0;    while(1) {        std::list<int>::iterator it;        for(it=li.begin(); it!=li.end(); ++it){            fd_set rfds;            FD_ZERO(&rfds);            int maxfd = 0;            int retval = 0;            FD_SET(*it, &rfds);            if(maxfd < *it){                maxfd = *it;            }            retval = select(maxfd+1, &rfds, NULL, NULL, &tv);//实现非阻塞式的通信,即需要等待时间的发生,一旦执行一定返回,返回的结果不同以表示函数执行的结果            if(retval == -1){                printf("select error\n");            }else if(retval == 0) {            }else{                char buf[1024];                memset(buf, 0 ,sizeof(buf));                long len = recv(*it, buf, sizeof(buf), 0);                printf("%s", buf);            }        }        sleep(1);            }}void sendMess() {    while(1) {        char buf[1024];        fgets(buf, sizeof(buf), stdin);//从文件流读取一行,送到缓冲区,使用时注意以下几点        std::list<int>::iterator it;        for(it=li.begin(); it!=li.end(); ++it){            send(*it, buf, sizeof(buf), 0);        }    }}int main() {    s = socket(AF_INET, SOCK_STREAM, 0);    memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(PORT);    servaddr.sin_addr.s_addr = inet_addr(IP);    if(bind(s, (struct sockaddr* ) &servaddr, sizeof(servaddr))==-1) {        perror("bind");        exit(1);    }    if(listen(s, 20) == -1) {        perror("listen");        exit(1);    }    len = sizeof(servaddr);    std::thread t(getConn);    t.detach();    std::thread t1(sendMess);    t1.detach();    std::thread t2(getData);    t2.detach();    while(1){            }    return 0;}

client:
#include <arpa/inet.h>#include <unistd.h>#include <iostream>#define MYPORT  7000#define BUFFER_SIZE 1024int main(int argc,char *argv[]){    int sock_cli;    fd_set rfds;    struct timeval tv;//设置时间    int retval, maxfd;        ///定义sockfd    sock_cli = socket(AF_INET,SOCK_STREAM, 0);    ///定义sockaddr_in    struct sockaddr_in servaddr;    memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family = AF_INET;    char s[1024];    int a;    std::cout << "输入想要建立连接的端口号以及IP地址:" << std::endl;    scanf("%d",&a);    getchar();    scanf("%s",s);    servaddr.sin_port = htons(a);  ///服务器端口,利用htons将主机字节顺序转换为网路字节数序从而进行数据包的传送    servaddr.sin_addr.s_addr = inet_addr(s);  ///服务器ip        //连接服务器,成功返回0,错误返回-1    if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)    {        perror("connect");        exit(1);    }        while(1){        /*把可读文件描述符的集合清空*/        FD_ZERO(&rfds);        /*把标准输入的文件描述符加入到集合中*/        FD_SET(0, &rfds);        maxfd = 0;        /*把当前连接的文件描述符加入到集合中*/        FD_SET(sock_cli, &rfds);        /*找出文件描述符集合中最大的文件描述符*/        if(maxfd < sock_cli)            maxfd = sock_cli;        /*设置超时时间*/        tv.tv_sec = 5;        tv.tv_usec = 0;        /*等待聊天*/        retval = select(maxfd+1, &rfds, NULL, NULL, &tv);//int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout);  监视我们需要的文件的文件描述符的变化情况——读写或是异常        if(retval == -1){            printf("select出错,客户端程序退出\n");            break;        }else if(retval == 0){//超时            printf("客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n");            continue;        }else{//文件可进行读写或者出错            /*服务器发来了消息*/            if(FD_ISSET(sock_cli,&rfds)){                char recvbuf[BUFFER_SIZE];                long len;                len = recv(sock_cli, recvbuf, sizeof(recvbuf),0);                printf("%s", recvbuf);                memset(recvbuf, 0, sizeof(recvbuf));            }            /*用户输入信息了,开始处理信息并发送*/            if(FD_ISSET(0, &rfds)){                char sendbuf[1024];//                scanf("%s",sendbuf);                fgets(sendbuf, sizeof(sendbuf), stdin);                send(sock_cli, sendbuf, strlen(sendbuf),0); //发送                memset(sendbuf, 0, sizeof(sendbuf));            }        }    }        close(sock_cli);    return 0;}