socket编程小细节

Posted ych9527

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket编程小细节相关的知识,希望对你有一定的参考价值。

1.inet_ntoa一些小细节

inet_ntoa 这个函数返回了个char*,是一个地址,即这个函数将转换的结果保存在了内存之中,然后返回对应的地址给调用者。

而我们并没有传入空间给这个函数,因此这个函数在内部一定申请了一块内存空间来保存ip转换的结果。这个返回结果保存到了静态存储区,不需要进行手动释放。

如果多次调用这个函数,由于是存储在自己内部的一个静态存储区,因此第二次调用时的结果会将上一次的覆盖掉

image-20210522073450863

在APUE中明确的提出inet_ntoa不是线程安全的函数(存储在静态区、所有的线程都可以看到)

那么在linux之下情况如何呢?

void* Func1(void* p)
{
  struct sockaddr_in* addr = (struct sockaddr_in*)p;
  while (1) 
  {
    char* ptr = inet_ntoa(addr->sin_addr);
    printf("addr1: %s\\n", ptr);
    sleep(1);
  }
  return NULL;
}

void* Func2(void* p) 
{
  struct sockaddr_in* addr = (struct sockaddr_in*)p;
while (1) 
{
  char* ptr = inet_ntoa(addr->sin_addr);
  printf("addr2: %s\\n", ptr);
  sleep(1);
}
return NULL;
}
int main() 
{
    pthread_t tid1 = 0;
    struct sockaddr_in addr1;
    struct sockaddr_in addr2;
    addr1.sin_addr.s_addr = 0;
    addr2.sin_addr.s_addr = 0xffffffff;

    pthread_create(&tid1, NULL, Func1, &addr1);

    pthread_t tid2 = 0;
    pthread_create(&tid2, NULL, Func2, &addr2);

    //等待线程
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}

image-20210522075533361

2.链接

2.1小知识

一个服务器有可能有多个客户端来进行链接,链接一旦多了起来,同样需要对其进行管理。管理是需要成本的,体现在时间和空间上,即服务器和客户端为了维护这个链接、在系统层面都要提供一批数据结构来管理这批链接(udp不需要做、因此速度相对更快)

CS模型:客户端主动发起链接、服务器被动,不会主动发起链接就叫做CS模型

BS模型:通过游览器去访问服务器、本质上也是CS模型

2.2建立链接的过程

调用socket,创建文件描述符

调用connect,向服务器发送连接请求

connect会发出SYN段并阻塞等待服务器的应答(第一次)

服务器收到客户端的SYN会应答一个SYN-ACK段表示"同意建立连接"(第二次)

客户端收到SYN-ACK后会从 connect返回,同时应答一个ACK段(第三次)

这个建立连接的过程就称为三次握手

connect是一个系统调用接口,调用之后并不是只有一个简单的动作,系统会自动进行三次握手的过程

image-20210522083817134

建立完连接、服务器和客户端就开始进行数据的传输:

TCP协议提供全双工的通信服务 ->在同一条链接之中,同一时刻,通信双方都可以同时写数据。
半双工:同一条链接,在同一时刻只能有一方来接数据(管道就是特殊的半双工(单向的))
类比一下:
全双工就是两个人在吵架、你不理我,我不理你,互相向对方"输出"
半双工就是:周瑜打黄盖、一方打,一方挨打,不能同时进行

服务器从accpet返回后立刻调用read(revf),读socket就像读取管道一样,如果没有数据到达就阻塞的等待

客户端调用write(send)发送请求给服务器,服务器通过read收到,对客户端的请求进行处理,在此期间客户端调用read等到服务器的应答

服务器调用write将处理结果发回给客户端,再次调用read阻塞等待下一条需求

客户端通过read收到请求,继续发送下一条请求,不断循环

断开连接的过程称为四次挥手:(服、客端都有权利断开连接,下面以客户端为例)

如果客户端没有更多的请求了,就调用close关闭连接,客户端会向服务器发送FIN段标志位报文(第一次)

服务器收到FIN后,会回应一个ACK,同时read也会返回0(第二次)

read返回之后,服务器就知道客户端关闭了链接,也会调用close关闭连接,这个时候服务器就会向客户端发送一个FIN(第三次)

客户端收到FIN,再返回一个ACK给服务器(第四次)

建立和断开连接是需要花费系统资源的(客户端和服务器都需要,技术层面上是对等的)

image-20210522091608493

3.TCP和UDP的对比

image-20210522092650020

4.如何理解套接字文件描述符

image-20210522105414088

image-20210522105512760

以上是关于socket编程小细节的主要内容,如果未能解决你的问题,请参考以下文章

golang代码片段(摘抄)

第一篇随笔,果真还是要注意细节。

Python 之 Socket编程(TCP/UDP)

C++,Java编程空指针的一个小细节

java Socket编程

Java Socket 编程