zoukankan      html  css  js  c++  java
  • 【网络编程01】socket的基础知识-简单网络通信程序

    1.什么是socket

      socket(套接字),简单来说是IP地址与端口(port)的组合,可以与远程主机的应用程序进行通信通过IP地址可以确定一台主机,而通过端口则可以确定某一个应用程序。IP+端口则可以完全确定某台主机的某个应用。socket起源于UNIX,类似一种特殊文件,可以进行打开,关闭,读写操作。总而言之,有了socket就可以与网络上的主机进行通信。

    2.TCP/UDP 协议

      要进行网络通信,就要进行一定规则约束,TCP/UDP就是这样的协议,规定了通信的规则。

      TCP是可靠的,面向连接的双向数据传输协议。可靠是指数据不会重复,也不会丢失。每当发送方发送一个数据给接收方时,如果接收方接收到了该数据,则会发送确认信息给发送方表示”我已经收到该数据了,你可以发送下一条数据了“,收到确认信息后,发送方才会发送下一条数据。这样就可以确定信息的无误。双向传输指双方都可以作为发送方或接收方。

      UDP是不可靠的,无连接的双向传输协议。UDP协议只管发送数据,不会确认你有没有收到,只负责发,不负责确认,所以是不可靠的。UDP适用于传输视频之类的,视频就算丢失一两帧也不会有太大影响。

      socket既可以是基于TCP,也可以是基于UDP的,根据需求选择即可。

    3.一个简单的通信程序

      用一个简单的例子来说明socket的用法。用socket写的程序一般分为,两部分,一个是服务器端,一个是客户端.

      下面说明服务器端创建过程

      1).首先要有套接字才能进行通信,创建套接字的函数是

     

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

      af:表示地址族,常用的有AF_INET表示使用IPV4地址,AF_INET6表示使用IPV6地址

      type:传输类型常用有SOCK_STREAM ,SOCK_DGRAM,流式传输,报文传输

      protocol:要使用的协议常用有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示TCP,UDP协议

      返回一个套接字描述符,也就是一个整型。

      2).用bind()函数确定socket各种属性

    1 int bind(int sock, struct sockaddr *addr, socklen_t addrlen);  

      sock:要绑定的套接字

      addr:SOCKADDR地址结构体,里面包含使用的协议,IP地址,端口等。要自己设定

      addrlen:SOCKADDR的大小,可以用sizeof()获取

      下面的代码展示创建一个套接字与绑定的过程

    1 //使用IPV4地址,TCP协议
    2 serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    3 SOCKADDR_IN addr;
    4 addr.sin_addr.S_un.S_addr = htonl(ADDR_ANY);//表示任何的ip过来连接都接受
    5 addr.sin_family = AF_INET;//使用IPV4地址
    6 addr.sin_port = htons(6666);//使用6666号端口
    7 bind(serverSocket, &addr, sizeof(SOCKADDR));//将套接字与端口6666,设定接收的ip绑定

     3).listen函数监听

      设定属性后,服务器端就可以开始监听了,监控是否有客户端请求连接。

      函数原型

    1 int listen(int sock, int backlog); 

      sock:套接字

      backlog:允许多少个客服端连接

     4).accept函数等待连接

      accept是一个阻塞函数,如果没有客户端清求连接会一直等待在这里

    1 int accept(int sock, struct sockaddr *addr, socklen_t *addrlen); 

      sock:套接字,

      addr:SOCKADDR 结构体

      addrlen:addr的长度,可以用sizeof求到

      要注意该函数的返回值,它会返回一个新的套接字,这个新的套接字是用来与客户端通信的套接字,之前那个套接字是监听的套接字,要分清楚。

      5).send/recv发送/接收信息

      与客户端连接成功后就可以进行通信了。可以通信的函数有write/read,send/recv等,这里介绍send/recv

    1 int send(int sockfd, const char *buf, size_t len, int flags);
    2 
    3 int recv(int sockfd, char*buf, size_t len, int flags);

      sockfd:套接字

      buf:发送数据的缓冲区

      len:发送数据的长度

      flags:标志,一般为零

      6).closesocket函数关闭套接字

      closesocket()关闭套接字

      下面是一个完整的服务器端的代码

     1 #include<stdio.h>
     2 #include<WinSock2.h>
     3 #pragma comment (lib,"ws2_32.lib")
     4 int main()
     5 {
     6     SOCKET serverSocket;//监视的套接字
     7     SOCKET newSocket;//用来通信的套接字
     8     SOCKADDR_IN newAddr;//保存客户端的socket地址信息
     9     SOCKADDR_IN addr;//地址结构体,包括ip port(端口)
    10 
    11     WSADATA data;    
    12     WORD version;//socket版本
    13     int info;
    14     char buf[32];//数据缓冲区
    15     /*
    16        在使用socket之前要进行版本的设定和初始化
    17        看不懂不用管
    18     */
    19     version = MAKEWORD(2, 2);//设定版本
    20     info = WSAStartup(version, &data);
    21     /*应用程序或DLL只能在一次成功的WSAStartup()调用之后
    22            才能调用进一步的Windows Sockets API函数。
    23         根据版本初始化 windows socket,返回0表示成功
    24     */
    25 
    26     if (info != 0)
    27     {
    28         printf("初始化失败
    ");
    29         return -1;
    30     }
    31     if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2)
    32     {    
    33         printf("加载失败
    ");
    34         WSACleanup();
    35         return 0;
    36     }
    37      //创建套接字,使用TCP协议
    38     serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    39     addr.sin_addr.S_un.S_addr = htonl(ADDR_ANY);//表示任何的ip过来连接都接受
    40     addr.sin_family = AF_INET;//使用ipv4的地址
    41     addr.sin_port = htons(6666);//设定应用占用的端口
    42     bind(serverSocket, &addr, sizeof(SOCKADDR));//将套接字与端口6666,接收的ip绑定
    43     listen(serverSocket, 3);//开始监听,是否有客服端请求连接
    44     printf("开始监听,等待连接..........
    ");
    45     int len = sizeof(SOCKADDR);
    46     newSocket=accept(serverSocket, (SOCKADDR*)&newAddr,&len);
    47     sprintf(buf, "欢迎:%s 的用户连接", inet_ntoa(newAddr.sin_addr));
    48     send(newSocket, buf, 32, 0);//发送信息
    49     printf("连接成功,开始发送信息..........
    ");
    50     recv(newSocket, buf, 32, 0);//接收信息
    51     printf("接收到的信息为:%s
    ", buf);
    52     closesocket(newSocket);//关闭套接字
    53 }

      运行结果

      

      客户端例子

      客户端与服务器端不同,服务器端是等待连接的,而客户端是主动连接的,所以客户端没有listen函数监听,也没有accept函数等待连接。

      客户端有一个connect函数用于主动连接服务器端。其余差不多

    1 int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);

      sock:套接字

      serv_addr:SOCKADDR结构体

      addrlen:serv_addr长度,可以用sizeof得到

      客户端代码

      

     1 #include<stdio.h>
     2 #include<WinSock2.h>
     3 #pragma comment(lib,"Ws2_32.lib")
     4 
     5 int main()
     6 {
     7     SOCKET clientSocket;
     8     SOCKADDR_IN addr;
     9     int len;
    10     char buf[32];
    11     int info;
    12     WSADATA data;
    13     WORD version;
    14     //设定版本,与初始化
    15     version = MAKEWORD(2, 2);
    16     info = WSAStartup(version, &data);
    17     if (info != 0)
    18     {
    19         printf("初始化失败
    ");
    20         return -1;
    21     }
    22     if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2)
    23     {
    24         printf("加载失败
    ");
    25         WSACleanup();
    26         return 0;
    27     }
    28     
    29     clientSocket = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    30     //要连接的服务器的ip,因为现在服务器端就是本机,所以写本机ip
    31     //127.0.0.1一个特殊的IP地址,表示是本机的IP地址
    32     addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    33     //端口要与服务器相同,不然找不到
    34     addr.sin_port = htons(6666);
    35     //用IPV4地址
    36     addr.sin_family = AF_INET;
    37     //主动连接服务器
    38     connect(clientSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR));
    39     //接收服务发送的数据
    40     recv(clientSocket, buf, 32, 0);//接收数据
    41     printf("服务器发送的信息是:%s
    ", buf);
    42     sprintf(buf, "%s","你好,服务器");
    43     //发送数据
    44     send(clientSocket, buf, 32, 0);
    45     //关闭套接字
    46     closesocket(clientSocket);
    47     return 0;
    48 
    49 }

      先启动服务器,再启动客户端。一次简单的通信就完成了

          

    把这个简单的例子做出来,对于socket应该会有初步的认识,最起码应该学会怎么用。

    下次利用socket写个简单的聊天程序,进一步加深对socket的认识。

  • 相关阅读:
    写了这么久前端,你知道浏览器原理吗?
    史上最全的web前端开发程序员学习清单!
    常见前端面试题及答案
    “下辈子再也不当程序员了,我被黑够了”
    什么是web前端,全栈工程师就业前景怎么样?
    要嫁就嫁程序员,钱多话少死得早!
    想转行做web前端工程师,必学这6大技能
    测试用例设计总结
    python读取一个英文文件,并记录每个单词出现的次数,降序输出
    python读取一个文件的每一行判断是否为素数,并把结果写到另一个文件中
  • 原文地址:https://www.cnblogs.com/duichoumian/p/12705832.html
Copyright © 2011-2022 走看看