当前位置: 首页 > Linux

小源泉linux网络编程--socket的创建

时间:2023-04-06 21:27:42 Linux

网络编程离不开socket。小源泉这篇文章详细讲解了socket的创建。认真学习这篇文章,对你理解网络底层会有非常重要的作用。同时即使已经有写好的模块可以使用了,这个东西还是要掌握的,大家一定要仔细阅读。TCP通信使用套接字的程序需要执行4个步骤。--分配socket和初始化--连接--发送或接收数据--关闭socket涉及的调用包括socket、bind、listen、connect(阻塞线程)、accept(阻塞线程)、recv(阻塞线程)、send(阻塞线程)。分配套接字和初始化-我们需要做的第一件事是分配套接字。--socket可以看作是一个文件描述符--不管是server端还是client端,第一步都是一样的。每个套接字都是一个通信通道。两个进程通过socket建立连接后,就可以再次发送和接收数据了。socket()intsocket(intdomain.inttyoe,intprotocol);系统调用socket有如下参数--intdomain--inttyoe--intprotocol(这个值一般为0)--成功返回socketDescriptor,失败返回-1,并设置errnosocket参数域说明AF_UNIXUNIX内部使用AF_INETTCP/IP协议AF_ISO国际标准组织协议AF_NSXerox网络协议类型描述SOCK_STREAM使用TCP进行可靠连接SOCK_DGRAM使用UDP进行不可靠连接bind()functionintbind(intsockfd,conststructsockaddr*my_addr,socklen_taddrlen);bind将一个进程与一个socket相关联,bind通常用于一个服务器进程建立一个socket来访问客户端连接(简单来说就是连接一个IP地址和端口号绑定在一起的程序)。参数sockfd是函数socket调用返回的套接字。参数my_addr是结构体sockaddr(用来描述IP地址的结构体)的地址。参数addrlen设置my_addr可以容纳的最大字节数。成功返回0,失败返回-1,并设置errno。一个端口号只能绑定一个程序。1对1的关系socklen_t本质上是一个unsignedint,而不是int。在Windows操作系统下是一个int。对于客户端程序,下一步是与要通信的服务器建立连接。--客户端只需要使用connect。对于服务端程序来说,需要建立自己的套接字,等待客户端的连接。--服务端需要调用listen和accept函数。listen()函数intlisten(intsockfd,intbacklog);创建socket并使用bind将其与进程关联后,服务端需要调用listen监听指定端口的客户端连接。参数sockfd是调用socket返回的socket描述符。参数backlog设置访问队列的大小。通常,将该值设置得足够大即可。参数backlog一般用于设置服务端最多可以并发接收来自客户端的连接数。listen也是从TCP缓存中读取连接,不是一一读取的。成功返回0,失败返回-1,并设置errnolisten将连接直接放入缓存区等待accept函数接收accept()函数intaccept(intsockfd,structsockaddraddr,socklen_taddrlen);当有客户端连接到服务器时,它们会排队等待,直到服务器准备好处理它们的位置,accept会返回一个新的socket,而原来的socket会继续监听指定的端口号参数sockfd是socket描述符调用socket返回的参数addr指向结构体sockaddr的地址,表示客户端的IP地址。参数addrlen设置addr可以容纳的最大字节数。成功返回一个新的socket,失败返回-1,设置errnoconnect()函数intconnect(intsockfd,conststructsockaddr*serv_addr,socklen_taddrlen);客户端调用connect连接到服务器。参数sockfd是调用socket返回的socket描述符。参数addr指向结构体sockaddr的地址。参数addrlen设置addr可以容纳的最大字节数。成功返回0,失败返回-1,并设置errno。connect()函数也被阻塞,它必须在返回前完成三次握手机制。客户端和服务端建立连接后,客户端和服务端之间就可以进行数据传输了,这需要两次系统调用。--send发送数据。--recv接收数据。套接字既可以发送数据也可以接收数据,网络是双向管道。send()函数ssize_tsend(ints,constvoid*buf,size_tlen,intflags);发送函数用于发送数据。参数s是已建立连接的套接字。参数buf为发送数据内存缓冲区地址指针。参数len表示缓冲区的大小,以字节为单位。参数flags一般填0。发送成功返回字节数,失败返回-1,并设置errno。注意send()函数1.send的返回值理解,在阻塞场景下,send的返回值要么是指定长度(发送成功),要么是-1,发送失败,但是在非-阻塞场景,返回值可能小于指定长度,这是因为发送的数据超出了发送缓冲区(窗口),所以只能发送缓冲区大小的数据,其余数据不能发送。2、errno=11的理解是send在非阻塞场景下可能返回-1,updateerrno11,11表示资源暂时不可用。当发送缓冲区满了,程序不断调用send(0函数发送数据时,就会出现这个错误。此时,当返回值为-1,errno=11时,需要停止发送数据,等待recv()函数ssize_trecv(ints,void*buf,size_tlen,intflags);recv函数用于接收数据,参数s是一个socket已经建立连接,参数buf为接收数据内存缓冲区的地址指针,参数len表示缓冲区的大小,单位字节,参数flags一般填0,成功则返回数字ofbytesreceived,如果失败则返回-1,并设置errno,如果peersocket关闭则返回0。recv函数只从TCP缓存中读取数据(此时数据已经在你的电脑上),不是直接从网络上,当TCP缓存满了对方的send函数就会停止发送数据。recv()函数将阻塞线程,直到收到消息或客户端关闭。最后,当您使用完套接字后,就该释放套接字占用的资源了。使用close执行此操作将在尝试向已关闭的套接字写入或读取数据时导致错误。setsockopt()函数intsetsockopt(ints,intlevel,intoptname,constvoid*optval,socklenoptlen);setsockopt函数如果socket函数设置成功返回0,失败返回-1,并设置errno常见用法为:inton=1;setsockopt(st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));由于TCP套接字状态TIME_WAIT,套接字在关闭后会保持2~4分钟左右。期间bind绑定端口失败。SO_REUSEADDR表示系统地址可以重复使用。当服务器在listen()函数后直接退出时,如果此时有客户端连接,则连接会被放入缓存中。如果此时启动一个新的程序来绑定IP地址和端口号,那么这个新的程序就会收到之前客户端的请求,这是有问题的,因为之前的客户端请求访问原程序。但是如果我们一直把这个IP地址和一个程序绑定在一起,就不需要这个TIME_WAIT信号了。在我们的服务器上,只有一台服务器绑定了这个IP地址和端口号,所以我们不需要这个机制,所以我们调用了setsockopt()函数。IP地址的结构IP地址在内存中用一个int表示,int在内存中占用4个字节的空间。第一个字节:192第二个字节:168第三个字节:1第四个字节:2Send(),recv()函数原理分析通过上一章网络编程1中主机之间的通信图,我们可以知道,当主机之间进行通信时两台主机,主机A先将报文打包成TCP数据包,然后将TCP数据包打包形成IP数据包,再形成以太网数据包发送。对方主机的接收顺序刚好相反。它首先接受以太网数据包,然后接收IP数据包,最后接收TCP数据包。在程序中通过测试后,recv()函数可以接受一次。数据(redhat最大可接收64K数据)高于send()函数发送的数据(redhat最大可发送数据128K以上)。时间是接收IP包,然后将IP包还原为TCP包,可见TCP包的容量实际上会大于等于IP包。其实电脑在发送数据的时候,如果TCP包过大,它会将TCP拆解成多个IP包,并将这些IP包存放在网卡的缓冲区中(如果send发送的数据超过缓冲区,sned()所在的线程会被挂起),recv在网卡上也有一个缓冲区,网络发送的数据会先存入缓冲区,直到缓冲区满,然后发送会停止发送,但这并不意味着send函数所在的线程会被挂起,send函数可以继续在自己的buffer中发送数据,直到buffer满为止。Linux中通过非阻塞socket接收数据时,经常会出现Resourcetemporarilyunavailable,errnocode为11(EAGAIN),说明你在非阻塞模式下调用了阻塞操作,如果操作未完成。这个错误是不会破坏socket的同步的,不用管它,recv之后会进行下一个循环。对于非阻塞套接字,EAGAIN不是错误。在VxWorks和Windows上,EAGAIN被称为EWOULDBLOCK。另外,如果出现EINTR,即errno为4,错误描述Interruptedsystemcall,错误是由于操作被信号通知,应该继续操作。最后,如果recv的返回值为0,则表示连接已经断开,我们的接收操作应该结束了。看到这里对大概的流程和概念理解的很深,下一节小编可以详细描述下操作流程,也就是通讯流程,一收一收,有点迷糊的可以去向小源泉寻找答案,了解的朋友可以期待小源泉的下一次更新!