zoukankan      html  css  js  c++  java
  • Socket网络编程

    windows下的socket网络编程

    已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考。

    windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作。还有windows下编译的时候需要连接ws32_lib库。

    大致过程如下

    • 1、初始化

      /*加载Winsock DLL*/
      WSADATA wsd;
      if (WSAStartup(MAKEWORD(2 , 2) , &wsd) != 0) {
      printf("Winsock 初始化失败! ");
      return 1;
      }

    • 2、socket相关函数调用

      socket(...)
      bind(...)
      listen(...)
      connect(...)
      accept(...)
      send/sendto
      recv/recvfrom

    • 3、清理
      WSACleanup();

    clinet.c 客户端

    客户端的流程很简单。

    • 1、先是使用socket函数产生一个打开的socket文件描述符。
    • 2、使用connect函数去连接服务端
    • 3、使用read/recv等读文件函数从服务端接收数据,使用write/send等写文件的函数向服务端发送数据

    上面是典型的TCP编程流程,如果是UDP的话不需要connect去连接服务端直接使用sendto函数来发送数据,使用recvfrom接收来自服务器的数据

    server.c 服务器端

    服务器端的流程比客户端稍微复杂一点

    • 1、调用socket打开一个socket句柄
    • 2、调用bind来绑定socket句柄到一个网口的某个端口
    • 3、调用listen来设置(启用)监听
    • 4、调用accept来等待客户端的连接

    上面是典型的TCP编程流程,如果是UDP的,那么不需要3,4这两部,直接使用recvfrom来接收客户端发过来的数据即可。

    UDP通信的实现

    我这里没有写TCP的,因为都是局域网内,就简单的写了个。
    这里是在虚拟机里面测试的截图,代码见最后。

    代码如下:https://www.cnblogs.com/oloroso/p/4613296.html

    关于详细的服务器建立的步骤以及相关的socket套接字的知识我已经在python socket编程的文章中提到过了,大家可以参看那一篇博客来历接socket套接字编程的内容,由于要是用C相关的API所以这里采用了基于C语言的socket API编写相关的网络编程内容,具体的实现如下所示,调试通过

    服务端Server.c程序内容:

    复制代码
      1 #include <sys/types.h>
      2 #include <sys/socket.h>
      3 #include <netinet/in.h>
      4 #include <arpa/inet.h>
      5 #include <netdb.h>
      6 #include <stdio.h>
      7 #include <errno.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 /************************************************************************************************************************
     12 1、int socket(int family,int type,int protocol)
     13 family:
     14     指定使用的协议簇:AF_INET(IPv4) AF_INET6(IPv6) AF_LOCAL(UNIX协议) AF_ROUTE(路由套接字) AF_KEY(秘钥套接字)
     15 type:
     16     指定使用的套接字的类型:SOCK_STREAM(字节流套接字) SOCK_DGRAM
     17 protocol:
     18     如果套接字类型不是原始套接字,那么这个参数就为0
     19 2、int bind(int sockfd, struct sockaddr *myaddr, int addrlen)
     20 sockfd:
     21     socket函数返回的套接字描述符
     22 myaddr:
     23     是指向本地IP地址的结构体指针
     24 myaddrlen:
     25     结构长度
     26 struct sockaddr{
     27     unsigned short sa_family; //通信协议类型族AF_xx
     28     char sa_data[14];  //14字节协议地址,包含该socket的IP地址和端口号
     29 };
     30 struct sockaddr_in{
     31     short int sin_family; //通信协议类型族
     32     unsigned short int sin_port; //端口号
     33     struct in_addr sin_addr; //IP地址
     34     unsigned char si_zero[8];  //填充0以保持与sockaddr结构的长度相同
     35 };
     36 3、int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen)
     37 sockfd:
     38     socket函数返回套接字描述符
     39 serv_addr:
     40     服务器IP地址结构指针
     41 addrlen:
     42     结构体指针的长度
     43 4、int listen(int sockfd, int backlog)
     44 sockfd:
     45     socket函数绑定bind后套接字描述符
     46 backlog:
     47     设置可连接客户端的最大连接个数,当有多个客户端向服务器请求时,收到此值的影响。默认值20
     48 5、int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen)
     49 sockfd:
     50     socket函数经过listen后套接字描述符
     51 cliaddr:
     52     客户端套接字接口地址结构
     53 addrlen:
     54     客户端地址结构长度
     55 6、int send(int sockfd, const void *msg,int len,int flags)
     56 7、int recv(int sockfd, void *buf,int len,unsigned int flags)
     57 sockfd:
     58     socket函数的套接字描述符
     59 msg:
     60     发送数据的指针
     61 buf:
     62     存放接收数据的缓冲区
     63 len:
     64     数据的长度,把flags设置为0
     65 *************************************************************************************************************************/
     66 int main(int argc, char *argv[])
     67 {
     68     int fd, new_fd, struct_len, numbytes,i;
     69     struct sockaddr_in server_addr;
     70     struct sockaddr_in client_addr;
     71     char buff[BUFSIZ];
     72 
     73     server_addr.sin_family = AF_INET;
     74     server_addr.sin_port = htons(8000);
     75     server_addr.sin_addr.s_addr = INADDR_ANY;
     76     bzero(&(server_addr.sin_zero), 8);
     77     struct_len = sizeof(struct sockaddr_in);
     78 
     79     fd = socket(AF_INET, SOCK_STREAM, 0);
     80     while(bind(fd, (struct sockaddr *)&server_addr, struct_len) == -1);
     81     printf("Bind Success!
    ");
     82     while(listen(fd, 10) == -1);
     83     printf("Listening....
    ");
     84     printf("Ready for Accept,Waitting...
    ");
     85     new_fd = accept(fd, (struct sockaddr *)&client_addr, &struct_len);
     86     printf("Get the Client.
    ");
     87     numbytes = send(new_fd,"Welcome to my server
    ",21,0); 
     88     while((numbytes = recv(new_fd, buff, BUFSIZ, 0)) > 0)
     89     {
     90         buff[numbytes] = '';
     91         printf("%s
    ",buff);
     92             if(send(new_fd,buff,numbytes,0)<0)  
     93             {  
     94                 perror("write");  
     95                 return 1;  
     96             }  
     97     }
     98     close(new_fd);
     99     close(fd);
    100     return 0;
    101 }
    复制代码

    客户端Client.c程序内容:

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    int main(int argc,char *argv[])
    {
        int sockfd,numbytes;
        char buf[BUFSIZ];
        struct sockaddr_in their_addr;
        printf("break!");
        while((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1);
        printf("We get the sockfd~
    ");
        their_addr.sin_family = AF_INET;
        their_addr.sin_port = htons(8000);
        their_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
        bzero(&(their_addr.sin_zero), 8);
        
        while(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr)) == -1);
        printf("Get the Server~Cheers!
    ");
        numbytes = recv(sockfd, buf, BUFSIZ,0);//接收服务器端信息  
        buf[numbytes]='';  
        printf("%s",buf);
        while(1)
        {
            printf("Entersome thing:");
            scanf("%s",buf);
            numbytes = send(sockfd, buf, strlen(buf), 0);
                numbytes=recv(sockfd,buf,BUFSIZ,0);  
            buf[numbytes]=''; 
            printf("received:%s
    ",buf);  
        }
        close(sockfd);
        return 0;
    }
    复制代码

    使用gcc编译器对客户端程序和服务端程序进行编译和解释:

    gcc -o Master Server.c
    gcc -o Slave Client.c

    编译的结果如下所示:

    这时请先运行Master程序,然后再运行Slave程序:

    在客户端Client输入要发送的内容:

     一、IP地址操作类
          1、IPAddress类
          a、在该类中有一个 Parse()方法,可以把点分的十进制IP表示转化成IPAddress类,方法如下:
          IPAddress address = IPAddress.Parse(“192.168.0.1”);

          b、IPAddress提供4个只读字段
          
          Any   用于代表本地系统可用的任何IP地址
          Broadcase 用于代表本地网络的IP广播地址
          Loopback 用于代表系统的回送地址    
          None 用于代表系统上没有网络接口

          其中IPAddress.Any最常用可以用来表示本机上所有的IP地址,这对于socket服务进行侦听时,最方便使用,不用对每个IP进行侦听了。而IPAddress.Broadcase可用来UDP的IP广播,这些具体讲socket时再详细介绍。

         2、IPEndPoint类            
          我们可以通过二种构造方法来创建IPEndPoint类:
          a、IPEndPoint(long address, int port)
          b、IPEndPoint(IPAddress address, int port)

          四个属性:
       
          Address
          AddressFamily
          Port
          MaxPort 
          MinPort 

          这些应该从名字上就很好理解,不再一一介绍。IPEndPoint其实就是一个IP地址和端口的绑定,可以代表一个服务,用来Socket通讯。

           二、DNS相关类
          DNS类有四个静态方法,来获取主机DNS相关信息,
          1、GetHostName() 
          通过Dns.GetHostName()可以获得本地计算机的主机名
       
          2、GetHostByName()
          根据主机名称,返回一个IPHostEntry 对象:
           
          IPHostEntry GetHostByName(string hostName) 

          其中IPHostEntry把一个DNS主机名与一个别名和IP地址的数组相关联,包含三个属性:
          *AddressList:一个IPAddress对象的数组
          *Aliases:一个字符串对象数组
          *HostName:一个用于主机名的字符串对象

          3、GetHostByAddress()

          类似于GetHostByName(),只不过这里的参数是IP地址,而不是主机名,也返回一个IPHostEntry对象。

          IPHostEntry GetHostByAddress(IPAddress address)
          IPHostEntry GetHostByAddress(string address)

          4、Resolve() 

          当我们不知道输入的远程主机的地址是哪种格式时(主机名或IP地址),用以上的二种方法来实现,我们可能还要通过判断客户输入的格式,才能正确使用,但Dns类提供一更简单的方法Resolve(),该方法可以接受或者是主机名格式或者是IP地址格式的任何一种地址,并返回IPHostEntry对象。

    复制代码
     1 extern int main_client(int , char**);
     2 extern int main_server(int , char**);
     3 
     4 int main(int c , char** v)
     5 {
     6     if (c == 3) {
     7         main_client(c , v);
     8     }
     9     else {
    10         main_server(c , v);
    11     }
    12     return 0;
    13 
    14 }
  • 相关阅读:
    【React Native】某个页面禁用物理返回键
    【React Native】DeviceEventEmitter监听通知及带参数传值
    转载【React Native代码】手写验证码倒计时组件
    【React Native】 中设置 APP 名称、应用图标、为安卓添加启动图
    【React Native错误集】* What went wrong: Execution failed for task ':app:installDebug'.
    【React Native错误集】Import fails with "Failed to execute 'ImportScripts' on 'WorkerGlobalScope'"
    【React Native错误集】Android error “Could not get BatchedBridge, make sure your bundle is packaged properly” on start of app
    「React Native笔记」在React的 setState 中操作数组和对象的多种方法(合集)
    【React Native】Error: Attribute application@allowBackup value=(false) from AndroidManifest.xml
    坚果云如何使用二次验证码/谷歌身份验证器/两步验证/虚拟MFA?
  • 原文地址:https://www.cnblogs.com/klb561/p/9011599.html
Copyright © 2011-2022 走看看