我们都知道socket函数执行成功时,返回一个可用的套接字描述符(套接字描述符实质上是一个无符号整数)。但每个进程维护一套套接字描述符还是每个线程维护一套呢?看下面的程序。

1. 不同进程同时使用socket

下面的代码socket_rst.c循环执行socket函数,申请套接字描述符。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MAX_NUM   10

int main(int argc, char *argv[])
{
	int	fd[MAX_NUM];
	int i;
	
	if (argc != 2)
	{
		printf("Usage: %s arg1n", argv[0]);
		return -1;
	}
	
	for (i = 0; i < MAX_NUM; i++)
	{
		if ((fd[i] = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		{
			printf("create socket %d errorn", i);
			return -1;
		}
		printf("%s: socket[%d] = %dn",argv[1], i, fd[i]);
		sleep(1);
	}

	return 0;
}

编译(gcc socket_rst.c -o socket_rst )后,我们同时启动两个进程,进程运行结果如下:

allan@ubuntu:workspace$ ./socket_rst process1 & ./socket_rst process2 &
[1] 17823
[2] 17824
allan@ubuntu:workspace$ process1: socket[0] = 3
process2: socket[0] = 3
process1: socket[1] = 4
process2: socket[1] = 4
process1: socket[2] = 5
process2: socket[2] = 5
process1: socket[3] = 6
process2: socket[3] = 6
process1: socket[4] = 7
process2: socket[4] = 7
process2: socket[5] = 8
process1: socket[5] = 8
process1: socket[6] = 9
process2: socket[6] = 9
process1: socket[7] = 10
process2: socket[7] = 10
process1: socket[8] = 11
process2: socket[8] = 11
process1: socket[9] = 12
process2: socket[9] = 12

[1]-  Done                    ./socket_rst process1
[2]+  Done                    ./socket_rst process2
allan@ubuntu:workspace$

可以看到两个进程的到的套接字描述符是相互没有影响的。

2. 不同线程同时使用socket

下面的代码socket_rst_thread.c中,进程创建一个线程,主线程(进程)和子线程中同时执行socket函数申请套接字描述符:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

#define MAX_NUM   10

void * thread_func()
{
	int	fd_thread[MAX_NUM];
	int i;

	for (i = 0; i < MAX_NUM; i++)
	{
		if ((fd_thread[i] = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		{
			printf("create socket %d errorn", i);
			exit(-1);
		}
		printf("Thread: socket[%d] = %dn", i, fd_thread[i]);
		sleep(1);
	}
}

int main(int argc, char *argv[])
{
	int fd[MAX_NUM];
	int i, status;
	pthread_t tid;
	void *exit_value;

	if (( status = pthread_create(&tid, NULL, thread_func, NULL)) != 0)
	{
		perror("Thread creation error.");
		return -1;
	}

	for (i = 0; i < MAX_NUM; i++)
	{
		if ((fd[i] = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		{
			printf("create socket %d errorn", i);
			return -1;
		}
		printf("Main: socket[%d] = %dn", i, fd[i]);
		sleep(1);
	}

	if ((status = pthread_join(tid, &exit_value)) != 0)
	{
		perror("Thread join error");
		return -1;
	}

	return 0;
}

编译(gcc socket_rst_thread.c -o socket_rst_thread -lpthread )后,程序运行结果如下:

allan@ubuntu:workspace$ ./socket_rst_thread 
Main: socket[0] = 3
Thread: socket[0] = 4
Main: socket[1] = 5
Thread: socket[1] = 6
Main: socket[2] = 7
Thread: socket[2] = 8
Main: socket[3] = 9
Thread: socket[3] = 10
Main: socket[4] = 11
Thread: socket[4] = 12
Thread: socket[5] = 13
Main: socket[5] = 14
Thread: socket[6] = 15
Main: socket[6] = 16
Main: socket[7] = 17
Thread: socket[7] = 18
Main: socket[8] = 19
Thread: socket[8] = 20
Main: socket[9] = 21
Thread: socket[9] = 22
allan@ubuntu:workspace$

可以看到主线程和子线程共用的是同一个套接字描述符集。

3. 结论

从上面的代码以及对比中,我们可以得到以下结论:

  1. 描述符0、1、2分别为系统使用的stdin、stdout、stderr。所以我们程序申请可用的(套接字)描述符最小从3开始。
  2. 申请(套接字)描述符时,系统总是(不考虑异常情况下)返回最小的可用的描述符(所以我们得到的描述符一般都是连续的)。
  3. 每个进程在自己的进程空间内维护一套自己的(套接字)描述符,进程内的线程共享这套描述符。不同进程间的描述符互不影响。