socket 订阅
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口 [1]  。 展开全文
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口 [1]  。
信息
外文名
socket [1]
类    型
流式套接字、数据报套接字和原始套接字 [3]
作    用
完成两个应用程序之间的数据传输 [3]
中文名
套接字 [1]
实    质
两个网络各自通信连接中的端点 [2]
学    科
计算机网络 [1]
套接字简介
套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序),各种进程使用这个相同的域互相之间用Internet协议簇来进行通信 [3]  。Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的API(应用程序编程接口),也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中,该 Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 Socket中,使对方能够接收到这段信息。 Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制 [2]  。
收起全文
精华内容
下载资源
问答
  • SocketTool调试软件是一款网络TCP/UDP通信调试工具,本工具集功能强大与简单易用为一体,是无需安装的免费绿色软件。 它可以帮助网络编程人员、网络维护人员检查所开发的网络应用软硬件的通信状况。 是一款非常好用...
  • Socket原理讲解

    万次阅读 多人点赞 2019-07-18 15:50:36
    对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问: 1.什么是TCP/IP、UDP? 2.Socket在哪里呢? 3.Socket是什么呢? 4.你会使用它们吗?什么是TCP/IP、UDP...

    对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问:

    1.         什么是TCP/IP、UDP?
    2.         Socket在哪里呢?
    3.         Socket是什么呢?
    4.         你会使用它们吗?

    什么是TCP/IP、UDP

             TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
             UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
            这里有一张图,表明了这些协议的关系。

                                                                                    

                                                                            图1

           TCP/IP协议族包括运输层、网络层、链路层。现在你知道TCP/IP与UDP的关系了吧。
    Socket在哪里呢?
           在图1中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。



    图2

           原来Socket在这里。
    Socket是什么呢?
           Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
    你会使用它们吗?
           前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。
           一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

          

    图3

           先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

    ============================================

    我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页 时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是 socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的。本文的主要内容如下:

    • 1、网络中进程之间如何通信?

    • 2、Socket是什么?

    • 3、socket的基本操作

      • 3.1、socket()函数

      • 3.2、bind()函数

      • 3.3、listen()、connect()函数

      • 3.4、accept()函数

      • 3.5、read()、write()函数等

      • 3.6、close()函数

    • 4、socket中TCP的三次握手建立连接详解

    • 5、socket中TCP的四次握手释放连接详解

    • 6、一个例子

    1、网络中进程之间如何通信?

    本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类:

    • 消息传递(管道、FIFO、消息队列)

    • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)

    • 共享内存(匿名的和具名的)

    • 远程过程调用(Solaris门和Sun RPC)

    但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

    使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。

    2、什么是Socket?

    上面我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。

    socket一词的起源

    在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。根据美国计算机历史博物馆的记载,Croker写道:“命名空间的元素都可称为套接字接口。一个套接字接口构成一个连接的一端,而一个连接可完全由一对套接字接口规定。”计算机历史博物馆补充道:“这比BSD的套接字接口定义早了大约12年。”

    3、socket的基本操作

    既然socket是“open—write/read—close”模式的一种实现,那么socket就提供了这些操作对应的函数接口。下面以TCP为例,介绍几个基本的socket接口函数。

    3.1、socket()函数

    int socket(int domain, int type, int protocol);
    

    socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

    正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket函数的三个参数分别为:

    • domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
    • type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。
    • protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议(这个协议我将会单独开篇讨论!)。

    注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

    当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

    3.2、bind()函数

    正如上面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    

    函数的三个参数分别为:

    • sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
    • addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:
      struct sockaddr_in {
          sa_family_t    sin_family; 
          in_port_t      sin_port;   
          struct in_addr sin_addr;   
      };
      
      
      struct in_addr {
          uint32_t       s_addr;     
      };
      
      ipv6对应的是:
      struct sockaddr_in6 { 
          sa_family_t     sin6_family;    
          in_port_t       sin6_port;      
          uint32_t        sin6_flowinfo;  
          struct in6_addr sin6_addr;      
          uint32_t        sin6_scope_id;  
      };
      
      struct in6_addr { 
          unsigned char   s6_addr[16];    
      };
      
      Unix域对应的是:
      #define UNIX_PATH_MAX    108
      
      struct sockaddr_un { 
          sa_family_t sun_family;                
          char        sun_path[UNIX_PATH_MAX];   
      };
      
    • addrlen:对应的是地址的长度。

    通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

    网络字节序与主机字节序

    主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:

      a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

      b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

    网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。

    所以: 在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于 这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再 赋给socket。

    3.3、listen()、connect()函数

    如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

    int listen(int sockfd, int backlog);
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    

    listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

    connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

    3.4、accept()函数

    TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

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

    accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

    注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。

    3.5、read()、write()等函数

    万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

    • read()/write()
    • recv()/send()
    • readv()/writev()
    • recvmsg()/sendmsg()
    • recvfrom()/sendto()

    我推荐使用recvmsg()/sendmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个函数。它们的声明如下:

           #include 
    
           ssize_t read(int fd, void *buf, size_t count);
           ssize_t write(int fd, const void *buf, size_t count);
    
           #include 
           #include 
    
           ssize_t send(int sockfd, const void *buf, size_t len, int flags);
           ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
           ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                          const struct sockaddr *dest_addr, socklen_t addrlen);
           ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);
    
           ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
           ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    
    

    read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。

    write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节 数。失败时返回-1,并设置errno变量。在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是 全部的数据。2)返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示 网络连接出现了问题(对方已经关闭了连接)。

    其它的我就不一一介绍这几对I/O函数了,具体参见man文档或者baidu、Google,下面的例子中将使用到send/recv。

    3.6、close()函数

    在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

    #include 
    int close(int fd);
    

    close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

    注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

    4、socket中TCP的三次握手建立连接详解

    我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

    • 客户端向服务器发送一个SYN J
    • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
    • 客户端再想服务器发一个确认ACK K+1

    只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:

    image

    图1、socket中发送的TCP三次握手

    从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

    总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。

    5、socket中TCP的四次握手释放连接详解

    上面介绍了socket中TCP的三次握手建立过程,及其涉及的socket函数。现在我们介绍socket中的四次握手释放连接的过程,请看下图:

    image

    图2、socket中发送的TCP四次握手

    图示过程如下:

    • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;

    • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;

    • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;

    • 接收到这个FIN的源发送端TCP对它进行确认。

    这样每个方向上都有一个FIN和ACK。

     

    6.下面给出实现的一个实例

     

    首先,先给出实现的截图

    服务器端代码如下:

     

     

     

    [cpp] view plaincopyprint?

    1. #include "InitSock.h"
    2. #include
    3. #include
    4. using namespace std;
    5. CInitSock initSock; // 初始化Winsock库
    6. int main()
    7. {
    8. // 创建套节字
    9. SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    10. //用来指定套接字使用的地址格式,通常使用AF_INET
    11. //指定套接字的类型,若是SOCK_DGRAM,则用的是udp不可靠传输
    12. //配合type参数使用,指定使用的协议类型(当指定套接字类型后,可以设置为0,因为默认为UDP或TCP)
    13. if(sListen == INVALID_SOCKET)
    14. {
    15. printf("Failed socket() \n");
    16. return 0;
    17. }
    18. // 填充sockaddr_in结构 ,是个结构体
    19. sockaddr_in sin;
    20. sin.sin_family = AF_INET;
    21. sin.sin_port = htons(4567); //1024 ~ 49151:普通用户注册的端口号
    22. sin.sin_addr.S_un.S_addr = INADDR_ANY;
    23. // 绑定这个套节字到一个本地地址
    24. if(::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    25. {
    26. printf("Failed bind() \n");
    27. return 0;
    28. }
    29. // 进入监听模式
    30. //2指的是,监听队列中允许保持的尚未处理的最大连接数
    31. if(::listen(sListen, 2) == SOCKET_ERROR)
    32. {
    33. printf("Failed listen() \n");
    34. return 0;
    35. }
    36. // 循环接受客户的连接请求
    37. sockaddr_in remoteAddr;
    38. int nAddrLen = sizeof(remoteAddr);
    39. SOCKET sClient = 0;
    40. char szText[] = " TCP Server Demo! \r\n";
    41. while(sClient==0)
    42. {
    43. // 接受一个新连接
    44. //((SOCKADDR*)&remoteAddr)一个指向sockaddr_in结构的指针,用于获取对方地址
    45. sClient = ::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);
    46. if(sClient == INVALID_SOCKET)
    47. {
    48. printf("Failed accept()");
    49. }
    50. printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
    51. continue ;
    52. }
    53. while(TRUE)
    54. {
    55. // 向客户端发送数据
    56. gets(szText) ;
    57. ::send(sClient, szText, strlen(szText), 0);
    58. // 从客户端接收数据
    59. char buff[256] ;
    60. int nRecv = ::recv(sClient, buff, 256, 0);
    61. if(nRecv > 0)
    62. {
    63. buff[nRecv] = '\0';
    64. printf(" 接收到数据:%s\n", buff);
    65. }
    66. }
    67. // 关闭同客户端的连接
    68. ::closesocket(sClient);
    69. // 关闭监听套节字
    70. ::closesocket(sListen);
    71. return 0;
    72. }

     


    客户端代码:

     

     

     

    [cpp] view plaincopyprint?

    1. #include "InitSock.h"
    2. #include
    3. #include
    4. using namespace std;
    5. CInitSock initSock; // 初始化Winsock库
    6. int main()
    7. {
    8. // 创建套节字
    9. SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    10. if(s == INVALID_SOCKET)
    11. {
    12. printf(" Failed socket() \n");
    13. return 0;
    14. }
    15. // 也可以在这里调用bind函数绑定一个本地地址
    16. // 否则系统将会自动安排
    17. // 填写远程地址信息
    18. sockaddr_in servAddr;
    19. servAddr.sin_family = AF_INET;
    20. servAddr.sin_port = htons(4567);
    21. // 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址
    22. // 如果你的计算机没有联网,直接使用127.0.0.1即可
    23. servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    24. if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
    25. {
    26. printf(" Failed connect() \n");
    27. return 0;
    28. }
    29. char buff[256];
    30. char szText[256] ;
    31. while(TRUE)
    32. {
    33. //从服务器端接收数据
    34. int nRecv = ::recv(s, buff, 256, 0);
    35. if(nRecv > 0)
    36. {
    37. buff[nRecv] = '\0';
    38. printf("接收到数据:%s\n", buff);
    39. }
    40. // 向服务器端发送数据
    41. gets(szText) ;
    42. szText[255] = '\0';
    43. ::send(s, szText, strlen(szText), 0) ;
    44. }
    45. // 关闭套节字
    46. ::closesocket(s);
    47. return 0;
    48. }

     


    封装的InitSock.h

     

     

     

    [cpp] view plaincopyprint?

    1. #include
    2. #include
    3. #include
    4. #include
    5. #pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
    6. class CInitSock
    7. {
    8. public:
    9. CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
    10. {
    11. // 初始化WS2_32.dll
    12. WSADATA wsaData;
    13. WORD sockVersion = MAKEWORD(minorVer, majorVer);
    14. if(::WSAStartup(sockVersion, &wsaData) != 0)
    15. {
    16. exit(0);
    17. }
    18. }
    19. ~CInitSock()
    20. {
    21. ::WSACleanup();
    22. }
    23. };
    展开全文
  • SuperSocket(服务端winform+客户端winform) 案例,可直接运行跑起来
  • C# winform Socket 即时通讯

    热门讨论 2017-10-27 11:20:21
    C# winform Socket 即时通讯,C# winform Socket 即时通讯
  • socket.io,socket.io-client 用于java或android连接nodejs websocket
  • 例子主要包括SocketAsyncEventArgs通讯封装、服务端实现日志查看、SCOKET列表、上传、下载、远程文件流、吞吐量协议,用于测试SocketAsyncEventArgs的性能和压力,最大连接数支持65535个长连接,最高命令交互速度...
  • 跨平台的socket库,windows-linux-socket

    热门讨论 2015-06-10 11:45:00
    跨平台的socket库,windows-linux-socket
  • Android基于Socket聊天最终版

    千次下载 热门讨论 2012-10-08 23:00:01
    Android之基于socket聊天小项目,包含服务器、客户端、数据库文件以及个人测试的截图。最后一次修改了,开始上班了,不弄了,谢谢大家。
  • Python+socket实现聊天程序

    千人学习 2018-02-07 21:13:36
    3小时学会使用Python开发聊天程序
  • Socket网络编程

    万次阅读 多人点赞 2021-03-22 13:34:47
    Socket 1 环境查看 通过cmd窗口的命令:ipconfig查看本机IP地址 查看网络情况是否正常:ping百度官网 用来进行本地测试的地址 127.0.0.1,回环测试地址,默认代表的就是本机的IP 2 Socket概述 socket编程也叫套接...

    Socket

    1 环境查看

    1. 通过cmd窗口的命令:ipconfig查看本机IP地址
      查看IP地址
    2. 查看网络情况是否正常:ping百度官网
      查看网络情况

    用来进行本地测试的地址 127.0.0.1,回环测试地址,默认代表的就是本机的IP

    2 Socket概述

    socket编程也叫套接字编程,应用程序可以通过它发送或者接受数据,可对其像打开文件一样打开/关闭/读写等操作.
    套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.
    网络套接字是IP地址与端口号TCP协议的组合
    Socket就是为网络编程提供的一种机制,通信的两端都有Socket
    网络通信其实就是Socket之间的通信,数据在两个Socket之间通过I/O进行传输.
    

    通信模式
    TCP网络通信模型

    3 服务器端

    3.1 ServerSocket

    在服务器端选择一个端口号,在指定端口上等待客户端发起连接

    构造方法:
    ServerSocket(int port) 创建绑定到特定端口的服务器套接字
    常用方法:
    Socket accept() 侦听并接收到此套接字的连接
    void close() 关闭此套接字
    启动服务:
    ServerSocket ss = new ServerSocket(端口号);
    等待客户端发起连接,连接后会建立起通道:Socket socket = ss.accept();

    4 客户端Socket

    我们经常使用的就是客户端

    构造方法:
    Socket(String host,int port) 创建一个流套接字,并将其连接到指定主机上的指定端口号
    常用方法:
    InputStream getInputStream() 返回此套接字的输入流
    OutputStream getOutputStream() 返回此套接字的输出流
    void close() 关闭此套接字
    新建Socket对象,连接指定IP指定的端口
    Socket s = new Socket(IP,port);
    从Socket获取双向的流:
    InputStream in = s.getInputStream();
    OutputStream out = s.getOutputStream();

    5 入门案例

    5.1服务器端代码编写

    需求: 服务器端接收客户端发来的数据”hello”,并且给客户端响应数据”world”

    创建包: cn.tedu.net
    创建类: Server.java

    package cn.tedu.net;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**本类用来表示Socket网络编程案例的服务器端
     * 完成步骤分析:
     * 1.启动服务器
     * 2.接收客户端的连接请求
     * 3.接收客户端发来的数据
     * */
    /**测试注意事项:先启动服务器端,再启动客户端*/
    public class Server {
    	public static void main(String[] args) throws Exception {
    		//1.启动服务器,指定端口号为8888,等待客户端的连接
    		/**注意:
    		 * 1.使用ServerSocket需要导包java.net.ServerSocket
    		 * 2.此操作会抛出异常
    		 * 3.指定的端口号范围是:0-655535,而0-1024是系统端口号,不能指定
    		 * */
    		ServerSocket ss = new ServerSocket(8888);
    		//2.接收客户端的连接请求,并建立数据通信通道
    		Socket socket = ss.accept();
    		//3.获取到读取流,接收并读取客户端发来的数据
    		InputStream in = socket.getInputStream();
    		//通过循环挨个读取显示读到的内容
    		for(int i = 0;i < 5;i++) {
    			//int b = in.read();//此方法读取的结果是把字符转成数字
    			char c = (char) in.read();//为了直接显示读取到的字符,需要强制类型转换(大转小,int转char)
    			System.out.print(c);//print()同行输出,注意细节哦
    		}
    		//5.给客户端发送数据
    		OutputStream out = socket.getOutputStream();
    		out.write("world".getBytes());
    		out.flush();
    		
    		//4.释放资源
    		/**注意关流的顺序,后出现的先关闭*/
    		in.close();
    		ss.close();
    	}
    }
    

    5.2 客户端代码编写

    创建包: cn.tedu.net
    创建类: Client.java

    package cn.tedu.net;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**本类用来表示Socket网络编程案例的客户端
     * 完成步骤分析:
     * 1.指定要连接的服务器
     * 2.给服务器发送hello
     * */
    public class Client {
    	public static void main(String[] args) throws Exception {
    		//1.指定要连接的服务器,需要同时指定服务器的IP & Port
    		/**注意:
    		 * 1.使用Socket需要导包java.net.Socket
    		 * 2.此操作会抛出异常
    		 * 3.如果使用的是本机的IP,地址是固定值,用来测试时使用127.0.0.1
    		 * */
    		Socket socket = new Socket("127.0.0.1",8888);
    		//2.给服务器端发送hello
    		OutputStream out = socket.getOutputStream();
    		//把要输出的数据hello字符串转变成byte[]的形式进行输出
    		out.write("hello".getBytes());
    		out.flush();
    		
    		//4.读取从服务器端返回的数据
    		InputStream in = socket.getInputStream();
    		for (int i = 0; i < 5; i++) {
    			char c = (char) in.read();//为了显示字符而不是数字,强制类型转换成char
    			System.out.print(c);//不换行展示获取到的数据
    		}
    		
    		//3.释放资源
    		//out.close();
    		socket.close();
    		
    	}
    }
    

    5.3 测试注意事项:

    1.测试顺序:先启动服务器端,再启动客户端
    2.可以打开多个控制台显示窗口,方便查看
    3.可以将其中一个控制台显示窗口固定,否则多个窗口的显示内容是一样的
    4.注意每次测试完毕后,都需要将之前的控制台窗口都关闭后再次进行新一轮的测试
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    注意:要先启动服务器端再启动客户端,否则会报错,如下所示:

    在这里插入图片描述

    注意,下次测试的时候,把上次测试的窗口都关掉,重复启动多次,正在使用,新次测试就无法使用

    在这里插入图片描述

    6 服务器端线程模型

    入门案例中,存在两种阻塞状态:

    1. 服务器端的accept()一直在等待客户端的连接
    2. 通信中的read()死等数据
      在这里插入图片描述

    7 读一行写一行案例

    创建包: cn.tedu.net
    创建类: TestIO.java

    package cn.tedu.net;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**这个类用来测试一行一行读取,一行一行写出*/
    public class TestIO {
    	public static void main(String[] args) {
    		//method();//初步测试readLine()
    		method2();//改进
    	}
    	public static void method2() {
    		BufferedReader in = null;
    		PrintWriter out = null;
    		try {
    			//1.读取一行数据,先拿到读取数据的流,注意文件需要自己在win创建
    			in = new BufferedReader(new FileReader("D:\\b.txt"));
    			//2.定义变量,记录每行读取到的数据
    			String line;
    			//3.设置循环读取数据,只要有数据就一直读取
    			while( (line=in.readLine()) != null) {
    				System.out.println(line);
    			}
    			
    			//4.一行行写出数据:PrintWriter
    			out = new PrintWriter(new FileWriter("D:\\b.txt"));
    			out.println("java");
    			out.println("hello");
    			out.flush();//为了防止有数据没有发送过去,可以刷一下
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally {
    			try {
    				//5.释放资源
    				out.close();
    				in.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	public static void method() {
    		try {
    			//1.读取一行数据,先拿到读取数据的流,注意文件需要自己在win创建
    			BufferedReader in = new BufferedReader(new FileReader("D:\\b.txt"));
    			//2.调用readLine方法进行测试
    			String line = in.readLine();
    			String line2 = in.readLine();
    			String line3 = in.readLine();
    			//测试1:b.txt没有数据时,readLine()返回null
    			//测试2:b.txt有数据,而且是1整行数据,readLine()可以返回整行全部数据
    			//测试3:b.txt有数据,而且是多行数据,readLine()只可以返回第1行全部数据
    			/**原因:readLine()在读取数据时,会读取特殊标记,换行符\r\n,读到换行符就结束
    			 * 结论:readLine()只可以读取1整行数据,如果是多行数据,需要多次调用
    			 * */
    			//3.打印测试结果
    			System.out.println(line);
    			System.out.println(line2);
    			System.out.println(line3);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    8 拓展 : 回声案例

    需求:接收客户端输入的每行数据,服务器再把回应给到用户

    8.1 服务器端

    接收客户端输入的每行数据,服务器再把回应给到用户

    创建包: cn.tedu.net
    创建类: Server2.java

    package cn.tedu.net;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**这个类用来当做回声案例的服务器端
     * 1.启动服务器
     * 2.接收客户端的连接请求
     * 3.给每个用户分配对应的话务员
     * 4.话务员:主要负责和对应的客户端对象进行沟通I/O
     * */
    /**1.创建优化后的Server2类,充当服务器端*/
    public class Server2 {
    	/**2.创建方法,负责服务多个客户*/
    	public void service() {
    		/**3.匿名对象+匿名内部类(重写run方法)*/
    		new Thread() {
    			/**5.把业务写在run()中*/
    			@Override
    			public void run() {
    				try {
    					//5.1 启动服务器,设置端口号为8000并等待客户端连接
    					ServerSocket ss = new ServerSocket(8000);
    					System.out.println("服务器启动成功!");
    					while(true) {//死循环,一直accept,也就是接受客户端的连接请求
    						//5.2 一直接受所有客户端的连接请求
    						Socket socket = ss.accept();
    						System.out.println("客户端连接成功!");
    						//5.3 给每个客户分配自己对应的话务员,1v1
    						HuaWuThread t = new HuaWuThread(socket);
    						t.start();
    					}
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    				
    			};
    		}.start();/**4.启动线程*/
    	}
    	
    	/**6.创建话务员内部类,主要负责和客户端沟通 I/O*/
    	class HuaWuThread extends Thread{
    		//6.1 定义本类中的成员变量socket,用来保持通话
    		Socket socket;
    		//6.2含参构造,接受当前的连接信息,保持通话,为谁服务就保存谁的数据
    		public HuaWuThread(Socket socket) {
    			this.socket = socket;
    		}
    		//6.3把话务员的业务放在run(),一直读取客户端发来的数据,并作出回应
    		@Override
    		public void run() {
    			try {
    				//7.1读取一行BufferedReader,并且写出一行PrintWriter --双向流
    				BufferedReader in = new BufferedReader(
    						new InputStreamReader(socket.getInputStream()));
    				PrintWriter out = new PrintWriter(
    						new OutputStreamWriter(socket.getOutputStream()));
    				//7.2读取客户端发来的一行数据
    				String line;//定义变量,记录读取到的一行数据
    				while((line = in.readLine()) != null) {//只要有数据就一直读
    					System.out.println("客户端发来的数据:"+line);
    					//7.1可以给客户端作出响应-接收键盘输入的响应
    					System.out.println("请输入您的回应:");
    					String input = new Scanner(System.in).nextLine();
    					//7.2发出作为服务器的响应
    					out.println(input);
    					out.flush();//把数据刷出去
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		Server2 s = new Server2();
    		s.service();
    	}
    }
    

    8.2 客户端

    接收客户端输入的每行数据,服务器再把回应给到用户

    创建包: cn.tedu.net
    创建类:Client2.java

    package cn.tedu.net;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**本类用来做回声案例的客户端
     * 1.连接指定的服务器
     * 2.给服务器发送数据
     * 3.接收服务器响应的数据
     * */
    public class Client2 {
    	public static void main(String[] args) {
    		try {
    			//1.连接指定的服务器,同时指定服务器的IP和端口号
    			Socket socket = new Socket("127.0.0.1",8000);
    			//2.给服务器发送数据
    			while(true) {
    				//向服务器写出一行数据,并且读取服务器响应回来的数据
    				PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
    				BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    				//3.通过流对象进行发送和读取的操作
    				System.out.println("请输入您想给服务器发送的数据:");
    				String input = new Scanner(System.in).nextLine();
    				out.println(input);//向服务端发送指定数据
    				out.flush();//把数据刷出去
    				String line = in.readLine();//读取回声数据
    				System.out.println("服务器端响应的数据是:"+line);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    展开全文
  • Windows下基于socket多线程并发通信的实现

    千次下载 热门讨论 2015-04-07 15:06:06
    本文介绍了在Windows 操作系统下基于TCP/IP 协议Socket 套接口的通信机制以及多线程编程知识与技巧,并给出多线程方式实现多用户与服务端(C/S)并发通信模型的详细算法,最后展现了用C++编写的多用户与服务器通信的...
  • C#Socket通信稳定完整版类库实例

    热门讨论 2016-03-26 13:53:25
    一款C#开发的稳定完整版的Socket通信应用实例
  •      本课程为JavaSE基础阶段的项目实战视频,主要使用技术均为JavaSE基础,手把手带你构建在线聊天室,需要JavaSE基础才能进行实战与开发,学习前需要掌握的技能有:逻辑基础、面向对象、异常处理、...
  • pc与android通过usb socket实现手机通信Demo

    千次下载 热门讨论 2013-08-01 20:02:06
    a.PC客户端与Android服务端的Socket同步通信(USB) b.操作步骤如下: ----------------------------------------------------------- 1.请将测试数据文件夹中的ucliulanqi.apk copy到c:\下进行测试 2. 将android...
  • c#socket异步及断线重连

    热门讨论 2016-01-01 23:41:27
    c#socket异步编程及断线重连。并怎样实现循环接受服务器的命令并处理。
  • 最新的C C++Socket技术总结! 开发环境:VS2010 实现功能: 1、运用多线程和Socket技术实现Socket Server端侦听多个客户端请求; 2、实现服务器端循环处理客户端不同请求从而实现不同测试要求,并向客户端循环发送...
  • 基于QT的socket TCP协议通信示例,包含客户端与服务端,实现了多线程、心跳机制、断线重连,数据简单封包(防止粘包)等必备技巧,已经封装为独立的类,可以移植性强,只需要添加自己的数据解析与处理即可直接使用。
  • node.js+socket.io离线包+聊天室源码

    热门讨论 2013-09-05 13:48:37
    这是基于socket.io实现双向实时通信的聊天室样例。来源于网上的博文,我对源码做了一些增强,并把需要的node.js、socket.io、jQuery、服务端和客户端源码,全部打在一起(node.js的npm需要在线安装,聊天室样例也有...
  • Socket文件传输

    热门讨论 2013-09-25 18:26:51
    本软件使用MFC采用面向对象的方法实现了基于Socket的文件传输。功能完整。
  • Android的socket长连接(心跳检测)

    千次下载 热门讨论 2014-12-18 17:10:54
    这是Android的socket长连接(心跳包),由于本人项目中需要用到长连接,所以先做了个demo,亲测是没有问题的。
  • SocketTool for Mac

    千次下载 热门讨论 2014-03-05 20:04:55
    TCP UDP SocketTool for Mac
  • 本课程包含了socket网络编程常用的所有特性,包括tcp、udp协议的基础编程,广播包,超时连接,多路复用,高并发的epoll多路复用,多线程的服务端。课程讲解理论与实践相结合,实践以代码延时讲解为主。
  • 移动开发 Android Socket Socket发送并接收服务器返回的数据 个人整理 。
  • Java实现Socket长连接和短连接

    千次下载 热门讨论 2014-09-21 11:32:43
    Java实现Socket长连接和短连接,实现原理可参见个人博客
  • 安卓作为服务端和移动端进行手机通信,亲测:运行没有问题!
  • python之socket编程

    万次阅读 多人点赞 2019-06-06 15:00:31
    Socket是什么呢? socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是...

    Socket是什么呢?

     

    socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

    基本上,Socket 是任何一种计算机网络通讯中最基础的内容。例如当你在浏览器地址栏中输入 http://www.cnblogs.com/ 时,你会打开一个套接字,然后连接到 http://www.cnblogs.com/ 并读取响应的页面然后然后显示出来。而其他一些聊天客户端如 gtalk 和 skype 也是类似。任何网络通讯都是通过 Socket 来完成的。

    Python 官方关于 Socket 的函数请看 http://docs.python.org/library/socket.html

    socket和file的区别:

      1、file模块是针对某个指定文件进行【打开】【读写】【关闭】

      2、socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】


           Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
    你会使用它们吗?
           前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。
           一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

     

    先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

    ===================================================

    下面是常用的方法:

    sk.bind(address)

      s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

    sk.listen(backlog)

      开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

          backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
          这个值不能无限大,因为要在内核中维护连接队列

    sk.setblocking(bool)

      是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

    sk.accept()

      接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。

      接收TCP 客户的连接(阻塞式)等待连接的到来

    sk.connect(address)

      连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

    sk.connect_ex(address)

      同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

    sk.close()

      关闭套接字

    sk.recv(bufsize[,flag])

      接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

    sk.recvfrom(bufsize[.flag])

      与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

    sk.send(string[,flag])

      将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

    sk.sendall(string[,flag])

      将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

          内部通过递归调用send,将所有内容发送出去。

    sk.sendto(string[,flag],address)

      将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

    sk.settimeout(timeout)

      设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

    sk.getpeername()

      返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

    sk.getsockname()

      返回套接字自己的地址。通常是一个元组(ipaddr,port)

    sk.fileno()

      套接字的文件描述符

    以下是例子:

    例子1:

    简单的服务器和客户端之间进行通讯

    service端:

    """
    file: service.py
    socket service
    """
    
    
    import socket
    import threading
    import time
    import sys
    
    
    def socket_service():
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 防止socket server重启后端口被占用(socket.error: [Errno 98] Address already in use)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind(('127.0.0.1', 6666))
            s.listen(10)
        except socket.error as msg:
            print(msg)
            sys.exit(1)
        print('Waiting connection...')
    
        while 1:
            conn, addr = s.accept()
            t = threading.Thread(target=deal_data, args=(conn, addr))
            t.start()
    
    def deal_data(conn, addr):
        print('Accept new connection from {0}'.format(addr))
        conn.send(('Hi, Welcome to the server!').encode())
        while 1:
            data = conn.recv(1024)
            print('{0} client send data is {1}'.format(addr, data.decode()))#b'\xe8\xbf\x99\xe6\xac\xa1\xe5\x8f\xaf\xe4\xbb\xa5\xe4\xba\x86'
            time.sleep(1)
            if data == 'exit' or not data:
                print('{0} connection close'.format(addr))
                conn.send(bytes('Connection closed!'),'UTF-8')
                break
            conn.send(bytes('Hello, {0}'.format(data),"UTF-8"))#TypeError: a bytes-like object is required, not 'str'
        conn.close()
    
    
    if __name__ == '__main__':
        socket_service()

    客户端:

    import socket
    import sys
    
    
    def socket_client():
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect(('127.0.0.1', 6666))
        except socket.error as msg:
            print(msg)
            sys.exit(1)
        print(s.recv(1024))#目的在于接受:Accept new connection from (...
        while 1:
            data = input('please input work: ').encode()
            s.send(data)
            print('aa',s.recv(1024))
            if data == 'exit':
                break
        s.close()
    
    
    if __name__ == '__main__':
        socket_client()

    例子二:

    进行文件的传输,如,.txt,.jpg等等

    服务器端:

    ###服务器端server.py
    import socket
    import os
    import sys
    import struct
    
    
    def socket_service_image():
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # s.bind(('127.0.0.1', 6666))
            s.bind(('127.0.0.1', 6666))
            s.listen(10)
        except socket.error as msg:
            print(msg)
            sys.exit(1)
    
        print("Wait for Connection.....................")
    
        while True:
            sock, addr = s.accept()  # addr是一个元组(ip,port)
            deal_image(sock, addr)
    
    
    def deal_image(sock, addr):
        print("Accept connection from {0}".format(addr))  # 查看发送端的ip和端口
    
        while True:
            fileinfo_size = struct.calcsize('128sq')
            print('fileinfo_size is',fileinfo_size)
            buf = sock.recv(fileinfo_size)  # 接收图片名
            print('buf is ',buf)
            if buf:
                filename, filesize = struct.unpack('128sq', buf)
                print('filename ,filesize is',filename.decode(),filesize )
                fn = filename.decode().strip('\x00')
                print('fn is ',fn)
                new_filename = os.path.join('./',
                                            'new_' + fn)  # 在服务器端新建图片名(可以不用新建的,直接用原来的也行,只要客户端和服务器不是同一个系统或接收到的图片和原图片不在一个文件夹下)
    
                recvd_size = 0
                fp = open(new_filename, 'wb')
    
                while not recvd_size == filesize:
                    if filesize - recvd_size > 1024:
                        data = sock.recv(1024)
                        recvd_size += len(data)
                    else:
                        data = sock.recv(1024)
                        recvd_size = filesize
                    print('data is',data)
                    fp.write(data)  # 写入图片数据
                fp.close()
            sock.close()
            break
    
    
    if __name__ == '__main__':
        socket_service_image()

    客户端:

    '''
    Fuction:客户端发送图片和数据
    Date:
    Author:mxh
    '''
    ###客户端client.py
    import socket
    import os
    import sys
    import struct
    
    
    def sock_client_image():
        while True:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.connect(('127.0.0.1', 6666))  # 服务器和客户端在不同的系统或不同的主机下时使用的ip和端口,首先要查看服务器所在的系统网卡的ip
                # s.connect(('127.0.0.1', 6666))  #服务器和客户端都在一个系统下时使用的ip和端口
            except socket.error as msg:
                print(msg)
                print(sys.exit(1))
            filepath = input('input the file: ')  # 输入当前目录下的图片名 xxx.jpg
            fhead = struct.pack(b'128sq', bytes(os.path.basename(filepath), encoding='utf-8'),
                                os.stat(filepath).st_size)  # 将xxx.jpg以128sq的格式打包
            s.send(fhead)
    
            fp = open(filepath, 'rb')  # 打开要传输的图片
            while True:
                data = fp.read(1024)  # 读入图片数据
                if not data:
                    print('{0} send over...'.format(filepath))
                    break
                s.send(data)  # 以二进制格式发送图片数据
            s.close()
            # break    #循环发送
    
    
    if __name__ == '__main__':
        sock_client_image()

    IO多路复用 

    I/O(input/output),即输入/输出端口。每个设备都会有一个专用的I/O地址,用来处理自己的输入输出信息首先什么是I/O:

    I/O分为磁盘io和网络io,这里说的是网络io

    IO多路复用:

    I/O多路复用指:通过一种机制,可以监视多个描述符(socket),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

    Linux

    Linux中的 select,poll,epoll 都是IO多路复用的机制。

    Linux下网络I/O使用socket套接字来通信,普通I/O模型只能监听一个socket,而I/O多路复用可同时监听多个socket.

    I/O多路复用避免阻塞在io上,原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理.

    Python  

    Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。

    对于select模块操作的方法:

    句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)

    参数: 可接受四个参数(前三个必须)

    返回值:三个列表

    select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。

    1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中

    2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中

    3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中

    4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化

    5、当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行

    例子1:

    服务端:
    sk1 = socket.socket()
    sk1.bind(("127.0.0.1",8001))
    sk1.listen()
    
    inpu = [sk1,]
    
    while True:
        r_list,w_list,e_list = select.select(inpu,[],[],1)
        for sk in r_list:
            if sk == sk1:
                conn,address = sk.accept()
                inpu.append(conn)
            else:
                try:
                    ret = str(sk.recv(1024),encoding="utf-8")
                    sk.sendall(bytes(ret+"hao",encoding="utf-8"))
                except Exception as ex:
                    inpu.remove(sk)
    
    客户端
    import socket
    
    obj = socket.socket()
    
    obj.connect(('127.0.0.1',8001))
    
    while True:
        inp = input("Please(q\退出):\n>>>")
        obj.sendall(bytes(inp,encoding="utf-8"))
        if inp == "q":
            break
        ret = str(obj.recv(1024),encoding="utf-8")
        print(ret)
    

    例子2:

    服务端:
    import socket
    sk1 = socket.socket()
    sk1.bind(("127.0.0.1",8001))
    sk1.listen()
    inputs = [sk1]
    import select
    message_dic = {}
    outputs = []
    while True:
    
        r_list, w_list, e_list = select.select(inputs,[],inputs,1)
        print("正在监听的socket对象%d" % len(inputs))
        print(r_list)
        for sk1_or_conn in r_list:
            if sk1_or_conn == sk1:
                conn,address = sk1_or_conn.accept()
                inputs.append(conn)
                message_dic[conn] = []
            else:
                try:
                    data_bytes = sk1_or_conn.recv(1024)
                    data_str = str(data_bytes,encoding="utf-8")
                    sk1_or_conn.sendall(bytes(data_str+"好",encoding="utf-8"))
                except Exception as ex:
                    inputs.remove(sk1_or_conn)
                else:
                    data_str = str(data_bytes,encoding="utf-8")
                    message_dic[sk1_or_conn].append(data_str)
                    outputs.append(sk1_or_conn)
            for conn in w_list:
                recv_str = message_dic[conn][0]
                del message_dic[conn][0]
                conn.sendall(bytes(recv_str+"好",encoding="utf-8"))
            for sk in e_list:
                inputs.remove(sk)
    
    客户端:
    import socket
    
    obj = socket.socket()
    
    obj.connect(('127.0.0.1',8001))
    
    while True:
        inp = input("Please(q\退出):\n>>>")
        obj.sendall(bytes(inp,encoding="utf-8"))
        if inp == "q":
            break
        ret = str(obj.recv(1024),encoding="utf-8")
        print(ret)

    参考:

    https://www.cnblogs.com/aylin/p/5572104.html

    https://www.cnblogs.com/wangcq/p/3520400.html

    展开全文
  • Socket

    千次阅读 热门讨论 2016-05-31 22:32:45
    Socket Socket Socket Socket Socket Socket Socket
     
    
    Socket
    Socket
    Socket
    Socket
    Socket
    Socket
    Socket
    展开全文
  • 董付国老师系列教材《Python程序设计(第2版)》(ISBN:9787302436515)、《Python可以这样学》(ISBN:9787302456469)配套视频,在教材基础上增加了部分案例,讲解Python 3.5.x和3.6.x中的Socket编程。
  • C#与Android Socket通信

    热门讨论 2014-02-09 13:51:03
    C#做服务器,Android做客户端,实现Socket通信

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,087,937
精华内容 435,174
关键字:

socket