前言:
线程(英文:thread),操作系统技术中的术语,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是行程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并行多个线程,每条线程并行执行不同的任务。在Unix SystemV及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
传输控制协议(TCP)是互联网协议组的主要协议之一。它起源于最初的网络实施,在网络实施中,它补充了互联网协议。因此,整个套件通常被称为TCP/IP。TCP在通过IP网络通信的主机上运行的应用程序之间提供可靠、有序且经过错误检查的八位字节流传递。万维网、电子邮件、远程管理和文件传输等主要互联网应用都依赖于TCP。不需要可靠数据流服务的应用程序可以使用用户数据报协议(User Datagram Protocol ,UDP),该协议提供无连接数据报服务,强调降低延迟而不是可靠性。
套接字,TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。套接字用(IP地址:端口号)表示,区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序),各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。
套接字又可分为三种类型:字节流套接字,数据报套接字,原始套接字。
一.函数源代码
1 socket()函数
socket函数是一种可用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源的函数。
int socket(int domain, int type, int protocol)
domain:一个地址描述。仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口所用的协议。如调用者不想指定,可用0指定,表示缺省。
socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。
对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。
2 bind()函数
用于未连接的数据报或流类套接口,把一本地地址与一套接口捆绑,在connect()或listen()调用前使用。
int bind(int sockfd, struct sockaddr *myaddr, int addrlen)
sockfd 表示已经建立的socket编号(描述符);
myaddr 是一个指向sockaddr结构体类型的指针;
在Internet地址族中,一个名字包括几个组成部分,对于SOCK_DGRAM和SOCK_STREAM类套接口,名字由三部分组成:主机地址,协议号(显式设置为UDP和TCP)和用以区分应用的端口号。如果一个应用并不关心分配给它的地址,则可将Internet地址设置为INADDR_ANY,或将端口号置为0。如果Internet地址段为INADDR_ANY,则可使用任意网络接口,且在有多种主机环境下可简化编程。如果端口号置为0,则Windows套接口实现将给应用程序分配一个值在1024到5000之间的唯一的端口。应用程序可在bind()后用getsockname()来获知所分配的地址,但必需注意的是,getsockname()只有在套接口连接成功后才会填写Internet地址,这是由于在多种主机环境下若干种Internet地址都是有效的。
3 服务器listen,客户端connect
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
listen函数应用于TCP连接的服务程序,它的作用是设置socket套接字允许等待来自客户端的连接请求。
isten函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。
4 accept函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
二 在linus下运行
命令如下
gcc -o server-while-tcp.out server-while-tcp.c
gcc -o client.out client.c
./server-while-tcp.out
./client.out 192.168.139.128
三 多线程
本实验创建了3个进程,为了更好的描述线程之间的并行执行,让3个线程共用同一个执行函数
编译:gcc thread.c -o thread -lpthread
执行:./thread