• 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 }
  • 相关阅读:
    用户数据报协议---UDP
    斐波那契数列
    从尾到头打印链表
    Mybatis三种查询方式
    Mybatis配置
    字典的用法
    遍历列表、切片、定义元组
    与列表相关知识
    python一些方法总结
    计算机的容量
  • 原文地址:https://www.cnblogs.com/klb561/p/9011599.html
Copyright © 2020-2023  润新知