Linux Socket基础介绍

Linux Socket函数库是从Berkeley大学开发的BSD UNIX系统中移植过来的。BSD Socket接口是众多Unix系统中被广泛支持的TCP/IP通信接口,Linux下的Socket程序设计,除了微小的差别之外,也适用于大多数其它Unix系统。

Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符

Socket的使用和文件操作比较类似。如同文件的读、写、打开、关闭等操作一样,TCP/IP网络通信同样也有这些操作,不过它使用的接口不是文件描述符或者FILE*,而是一个称做Socket的描述符。类似于文件操作,对于Socket,也通过读、写、打开、关闭操作来进行网络数据传送。同时,还有一些辅助的函数,如域名/IP地址查询、Socket功能设置等。

套接字有三种类型:流式套接字、数据报套接字及原始套接字。

流式套接字定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠、无差错。原始套接字允许对底层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等。

套接字工作过程如下服务器首先启动,通过调用socket()建立一个套接字,然后调用bind()将该套接字和本地网络地址联系在一起,再调用listen()使套接字做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接。客户在建立套接字后就可以调用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据。最后,待数据传送结束后,双方调用close()关闭套接字。

Socket函数库

1.        socket(int domain, int type,int protocol):此函数分配一个Socket句柄,用于指定特定网络下,使用特定的协议和数据传送方式进行通信。Socket句柄分配以后,如果要开始TCP通信,还需要建立连接。根据需要,可以主动地建立连接(通过connect())和被动地等待对方建立连接(通过listen()),在连接建立后才能使用读写操作通过网络连接进行数据交换。

domain参数选择通信中使用的协议族,也就是网络的类型,可以是以下之一:(1)、AF_UNIX:UNIX内部协议;(2)、AF_INET:ARPA Internet协议,也就是TCP/IP协议族;(3)、AF_ISO:ISO协议;(4)、AF_NS:Xerox Network Systems协议;(5)、AF_IMPLINK:IMP “host at IMP” link layer。

type参数为数据传送的方式,可以是以下之一:(1)、SOCK_STREAM:保证顺序的、可靠传送的双向字节数据流,最为常用,也是TCP连接所使用的方式;(2)、SOCK_DGRAM:无连接的、不保证可靠的、固定长度(通常很小)的消息传送;(3)、SOCK_SEQPACKET:顺序的、可靠的双向固定长度的数据报传送,只用于AF_NS类型的网络中;(4)、SOCK_RAW:原始的数据传送,适用于系统内部专用的网络协议和接口,和SOCK_RDM一样,只能由超级用户使用;(5)、SOCK_RDM:可靠的数据报传送,未实现。

protocol参数指定通信中使用的协议。在给定Socket的协议族和传送类型之后,一般情况下所使用的协议也就固定下来,此时protocol参数可使用缺省值”0”。但如果还有多个协议供选择,则必须使用protocol参数来标识。

socket()函数,正常执行时,返回Socket描述符;否则,返回-1,错误状态在全局变量errno中。

2.        close(int fd):Socket和文件描述符的关闭操作都是使用这个函数。fd参数为Socket描述符。返回值,正常是返回0,-1表示出错。

3.        bind(int sockfd, structsockaddr *my_addr, int addrlen):此函数给已经打开的Socket指定本地地址。这个函数的使用有以下两种情况:(1)、如果此Socket是面向连接的,而且此Socket在连接建立过程中处于被动的地位,即,乙方程序使用listen函数等待对方建立连接,对方用connect函数来向此Socket建立连接,这种情况下,必须用bind给此Socket设定本地地址。在乙方使用listen函数时,除指定Socket描述符之外,该Socket必须已经用bind函数设定好了本地地址(包括IP地址和端口号),这样,系统在收到建立连接的网络请求时,才能根据请求的目的地址,识别是通向哪个Socket的连接,从而乙方才能用此Socket接收到发给此Socket地址的数据包。不指定Socket的本地地址,就无法将此Socket用于连接建立和数据接收。(2)、如果此Socket用于无连接的情形,同样也要求给该Socket设定本地地址,这样,以后系统从网络中接收到数据后,才知道该送给哪个Socket及其相对应的进程。

sockfd参数:用于指定Socket描述符。

addrlen参数:my_addr结构的长度。

my_addr参数:用于侦听连接请求的本地地址。struct sockaddr是一个通用性的结构,不仅包含TCP/IP协议的情况,同时也是为了适合于其它网络,如AF_NS。由于它的这种通用性,它只是定义了一个一般意义上的存储空间。

bind函数返回值:正常时返回0,否则返回-1,同时errno是系统错误码。

4.        listen(int s, int backlog):准备接收连接请求。在用bind()给一个Socket设定本地地址后,就可以将这个Socket用于接收连接请求,即listen()。调用listen()之后,系统将给此Socket配备一个连接请求的队列,暂存系统接收到的、申请向此Socket建立连接的请求,等待用户程序用accept()正式接收该请求。队列长度,就由backlog参数指定。如果短时间内向乙方建立连接的请求过多,乙方来不及处理,那么排在backlog之后的请求将被系统拒绝。因此,backlog参数实际上规定了乙方程序能够容许的连接建立处理速度。至于乙方程序使用此Socket(及其指定的本地地址)实际建立连接的个数,由乙方程序调用accept()的次数来决定。

listen函数返回值:正常时返回0,否则返回-1.

5.        accept(int s, struct sockaddr*addr, int *addrlen):接受指定Socket上的连接请求。在调用listen()之后,系统就在Socket的连接请求暂存队列里存放每一个向该Socket(及其本地地址)建立的连接请求。accept()函数的作用就是,从该暂存队列中取出一个连接请求,用该Socket的数据,创建一个新的Socket:Socket_New,并为它分配一个文件描述符。Socket_New即标识了此次建立的连接,可被乙方用来向连接的另一方发生和接收数据(write/read, send/recv)。同时,原Socket仍然保持打开状态不变,继续用于等待网络连接请求。

如果该Socket的暂存队列中没有待处理的连接请求,根据Socket的特征选项(是否non_blocking),blocking即阻塞,accept()函数将选择两种方式:如果该Socket不是non_blocking型的,accept()将一直等待,直到收到一个连接请求后才返回;如果该Socket是non_blocking型的,那么accept()将立即返回,但如果没有连接请求,只返回错误信息,不创建新的Socket_New。accept()返回后,如果创建了新的Socket_New来标识新建立的连接,那么参数addr指定的结构里面将会有对方的地址信息,addrlen是地址信息的长度。

s参数:Socket描述符。

addr参数:accept()接受连接后,在addr指向的结构中存放对方的地址信息。如果是AF_INET Socket,该地址信息就是地方的IP地址和端口号。

addrlen参数:在调用accept()之前,*addrlen必须被设置为addr数据结构的合法长度。在accept()返回之后,*addrlen中是对方地址信息的长度。

accept函数返回值:如果正常创建了一个新的连接,那么返回非负的整数,即新连接的Socket描述符(注意,用于等待连接请求的原Socket保持打开状态不变,可用于接收新的连接请求),否则,返回-1.

bind、listen、connect等函数,都是用于被动地等待对方建立连接时需要使用的,而connect函数,则是主动地向对方建立连接时使用的。connect()使用一个事先打开的Socket,和目的方(即通信对方,或称服务器一方)地址信息,向对方发出连接建立请求。

6.        connect(int sockfd, structsockaddr *serv_addr, int addrlen):客户端发送服务请求。

sockfd参数:socket函数返回的socket描述符。

serv_addr:存储远程计算机的IP地址和端口信息的结构。

addrlen:是结构体sockaddr_in的长度。

返回值:成功返回0,否则返回-1.

7.        send(int s, const void *msg,int len, unsigned int flags)/sendto(int s, const void *msg, int len, unsignedint flags, const struct sockaddr *to, int tolen),recv(int s, void*buf, int len, unsigned iint flags)/recvfrom(int s, void *buf, int len,unsigned int flags, struct sockaddr *from, int *fromlen):用Socket发送和接收数据。在连接建立完成后,通信双方就可以使用以上这些函数来进行数据的发送和接收操作。其中,send和recv用于连接建立以后的发送和接收。sendto和recvfrom用于非连接的协议。对于非non_blocking型的Socket,send将等待数据发送完后才返回;对于non_blocking型的Socket,send将立即返回,用户程序需要用select()函数决定网络发送是否结束。类似地,对于非non_blocking型的Socket,若系统没有收到任何数据,recv将等待接收数据到达后才返回;对于non_blocking型的Socket,recv将立即返回,并返回错误信息或接收到的数据字节数。sendto和recvfrom因为是非连接型的发送和接收,必须在参数中给出目的地址或者存放源地址的空间。

s参数:Socket描述符。

msg,buf参数:存放接收或者发送数据的存储空间。

len参数:接收或者发送数据的字节数。

to,from参数:sendto和recvfrom所使用的,目的方地址和存放源地址的空间。

tolen,fromlen参数:目的地址和源地址空间大小。

flag参数:通常设为0.

返回值:send/sendto返回实际发送的数据字节数,或者-1,表示出错。recv/recvfrom返回实际接收到的数据字节数,或者-1,表示出错。

8.        read(int fd, void *buf, size_tcount)/write(int fd, const void *buf, size_t count):用系统文件操作进行Socket通信。在连接建立完成后,对于连接建立过程中被动的一方,在accept()正常返回后,它返回一个新的Socket,并且为该Socket分配了一个文件描述符;对于连接请求发起方,connect()正常返回后,相应的Socket中也包含有已分配的文件描述符。因此,可以使用标准的Unix文件读写函数read()/write()来进行Socket通信。要注意的是,由于网络数据和磁盘文件不一样,不是已经准备好的,因此,每次读写操作不一定能传送完指定长度的数据,需要由程序反复进行剩余部分的传送。另外,文件描述符是较底层的文件操作函数,不同于C语言中常用的FILE*。FILE*是使用fread/fwrite函数来进行读写操作的。

fd参数:文件或者Socket描述符。

buf参数:数据缓冲区。

count参数:数据字节数。

函数返回值:正常时,返回所读写的字节数(注意,可能小于count参数指定的数目);否则,返回-1.

9.        getsockopt(int s, int level,int optname, void *optval, int *optlen)/setsockopt(int s, int level, intoptname, const void *optval, int optlen):获取、设置Socket特征选项。

常用的几个转换函数:(1)、inet_addr:将IP地址从点数格式转换成无符号长整型,它返回的地址是网络字节格式;(2)、inet_ntoa:将一个in_addr结构体输出成点数格式;(3)、htonl:将32位的主机字节顺序转化为32位的网络字节顺序;(4)、htons:将16位的主机字节顺序转化为16位的网络字节顺序;(5)、ntohs:将一个无符号短整形数从网络字节顺序转化为主机字节顺序;(6)、ntohl:将一个无符号长整形数从网络主机顺序转化为主机字节顺序。

以下是测试用例:

1. client.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <iostream>

#define PORT 7000

int main()
{
	struct sockaddr_in server;
	int s, ns;
	int pktlen, buflen;
	char buf1[256], buf2[256];

	s = socket(AF_INET, SOCK_STREAM, 0);
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htons(INADDR_ANY);

	if (connect(s, (struct sockaddr*)&server, sizeof(server)) < 0) {
		perror("connect()");
		return -1;
	}

	for (;;) {
		printf("Enter a line: ");
		std::cin>>buf1;
		buflen = strlen(buf1);
		if (buflen == 1)
			break;

		send(s, buf1, buflen+1, 0);
		recv(s, buf2, sizeof(buf2), 0);
		printf("Received line: %s\n", buf2);
	}

	close(s);

	return 0;
}

2. server.cpp:

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>

#define PORT 7000

int main()
{
	struct sockaddr_in client, server;
	int s, ns, pktlen;
	char buf[256];

	s = socket(AF_INET, SOCK_STREAM, 0);
	memset((char*)&server, sizeof(server), 0);
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htons(INADDR_ANY);

	bind(s, (struct sockaddr*)&server, sizeof(server));

	listen(s, 1);

	socklen_t namelen = sizeof(client);
	ns = accept(s, (struct sockaddr*)&client, &namelen);

	for (;;) {
		pktlen = recv(ns, buf, sizeof(buf), 0);
		if (pktlen == 0)
			break;
		printf("Received line:%s\n", buf);

		for (int i = 0; i < strlen(buf); i++)
			buf[i] = toupper(buf[i]);

		send(ns, buf, pktlen, 0);
	}

	close(ns);
	close(s);

	return 0;
}

执行说明:(1)、打开终端,分别执行:$ g++ -o server server.cpp  , $ g++ -o client client.cpp ; (2)、打开终端,先执行服务器端程序:$ ./server ;再打开另一终端,执行客户端程序:$ ./client  ;(3)、程序功能:服务器端接收从客户端来的数据,并将其接收的数据小写字母改为大写再发送给客户端,在客户端显示接收后的结果数据。当输入一个字符长度时退出。

以上部分内容整理自网络。

时间: 2024-11-06 15:17:40

Linux Socket基础介绍的相关文章

linux日志基础介绍

linux上常见的日志有:1./var/log/cron 查询crontab有没有被执行,是否出现问题.2./var/log/dmesg 记录开机的时候内核检测过程所产生的各项信息.3./var/log/lastlog 记录系统上面所有的帐号最近一次登录系统时的信息.4./var/log/secure 记录登录linux的所有帐号密码的信息.5./var/log/messages 系统发生的错误或者重要信息都会记录到此文件 日志文件产生:1.软件开发商自行定义写入的日志,如apache2.有li

linux socket基础

一个简单的客户端获取服务器时间的例子: 服务器代码: #include <iostream> #include "unp.h" #include "my_err.h" #define DEFAULT_PORT 8000 int main(int argc, const char * argv[]) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[MAXLINE]; time_t

linux入门基础知识及简单命令介绍

linux入门基础知识介绍 1.计算机硬件组成介绍 计算机主要由cpu(运算器.控制器),内存,I/O,外部存储等构成. cpu主要是用来对二进制数据进行运算操作,它从内存中取出数据,然后进行相应的运算操作.不能从硬盘中直接取数据. 内存从外部存储中取出数据供cpu运存.内存的最小单位是字节(byte) 备注:由于32的cpu逻辑寻址能力最大为32内存单元.因此32位cpu可以访问的最大内存空间为:4GB,算法如下: 2^32=2^10*2^10*2^10*2^2 =1024*1024*1024

&lt;转&gt;Socket编程——基础介绍

最近系统的看了下unix网络编程的一些内容,对socket的理解有了进一步的加深,在看APUE的时候,那会儿看socket上面介绍的比较少,只是模糊的懂了如何去写一个简单的TCP服务端和客户端,对其中一些注意的点,以及实现的原理没有过多的去研究.这是我自己总结的socket编程的第一篇,基本就是介绍一些基础性的东西. 这个只是在IPV4上的一些socket编程,对于IPV6暂不涉及.下面对unix网络编程卷一第三版简称为unpV13e 地址结构 提到地址结构我们一般使用的是最基本的地址结构.IP

快速学习C语言三: 开发环境, VIM配置, TCP基础,Linux开发基础,Socket开发基础

上次学了一些C开发相关的工具,这次再配置一下VIM,让开发过程更爽一些. 另外再学一些linux下网络开发的基础,好多人学C也是为了做网络开发. 开发环境 首先得有个Linux环境,有时候家里机器是Windows,装虚拟机也麻烦,所以还不如30块钱 买个腾讯云,用putty远程练上去写代码呢. 我一直都是putty+VIM在Linux下开发代码,好几年了,只要把putty和VIM配置好,其实 开发效率挺高的. 买好腾讯云后,装个Centos,会分配个外网IP,然后买个域名,在DNSPod解析过去

Linux零基础入学之1-1课程介绍&了解RHEL7&安装RHEL7

[本节内容] * 课程介绍 * RHEL7了解 * RHEL7.2的安装 * 实战:组装服务器 [Linux介绍] 服务器种类:刀片式.塔式(机架式) 1U:4.45cm   三指宽   指服务器的高度 贝尔实验室    Unix    肯·汤普森 & 丹尼斯·里奇 二人合作用汇编语言完成编写Unix.1972年C语言诞生.1973年,二人又用C语言重新编写 了Unix,以实现Unix系统数据的可迁移性. 加州大学伯莱利分校BSD编写4.4 free BSD UNIX 1991年,Linux正式

第一周博客--计算机操作系统及Linux基础介绍

1.描述计算机的组成及其功能 2.按系列罗列Linux的发行版,并描述不同发行版之间的联系与区别 3.描述Linux的哲学思想,并按照自己的理解对其进行解释性描述 4.说明Linux系统上命令的使用格式:详细介绍ifconfig.echo.tty.startx.export.pwd.history.shutdown.powrof.reboot.hwclock.date命令的使用,并配合相应的示例来阐述 5.如何在Linux系统上获取命令的帮助信息,请详细列出,并描述man文档的章节是如何划分的

Linux的简单介绍和基础命令(上)

一.Linux简要介绍 Linux命令基础 Linux命令帮助 目录与文件基本操作 Linux家族 Redhat 红帽 三个认证 (开源但是不免费)--->社区(系统开发者)--->Centos(服务器端) Ubuntu 最好的客户端系统,开源纯免费(软件开发人群) Debian --->kali Linux(专用工具资源占用很少) 树莓派 suse Linux--->ISP(电信,移动,联通) 定制版 shell--Linux系统的一种特殊程序--"翻译官"

Linux基础介绍【第八篇】

Linux网络基础 网线 568A 568B 线序:橙白橙 绿白蓝 蓝白绿 棕白棕 交换机.路由器 交换机:DLINK.H3C.CISCO 交换机(Switch)是一种用于电信号转发的网络设备.它可以为接入交换机的任意两个网络节点提供独享的电信号通路.最常见的交换机是以太网交换机.其他常见的还有电话语音交换机.光纤交换机等. 路由器:CISCO 路由器(Router)是连接因特网中各局域网.广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号. OSI7层网络模型