原文:http://blog.csdn.net/qq_29350001/article/details/73176210
一、基本概念
1、编程接口
什么是伯克利套接字(Berkeley Socket)?
美国加利福尼亚大学比克利分校于 1983年发布 4.2 BSD Unix 系统。其中包含一套用 C 语言编写的应用程序开发库。该库既可用于在同一台计算机上实现进程间通信,也可用于在不同计算机上实现网络通信。当时的 Unix 还受 AT&T 的专利保护,因此直到 1989 年,伯克利大学才能自由发布他们的操作系统和网络库,而后者即被称为伯克利套接字应用编程接口(Berkeley Socker APIs)。
伯克利套接字接口的实现完全基于 TCP/IP 协议,因此它是构建一切互联网应用的基石。几乎所有现代操作系统都或多或少有一些源自伯克利套接字接口的实现。它已成为应用程序连接互联网的标准接口。
什么是套接字?
套接字(socket)的本意是指电源插座,这里将其引申为一个基于 TPC/IP 协议可实现基本网络通信功能的逻辑对象。
机器与机器的通信,或者进程与进程的通信,在这里都可以被抽象地看作是套接字与套接字的通信。
应用程序编写者无需了解网络协议的任何细节,更无需知晓系统内核和网络设备的运作机制,只要把想发送的数据写入套接字,或从套接字中读取想接收的数据即可。
从这个意义上讲,套接字就相当于一个文件描述符,而网络就是一种特殊的文件,面向网络的编程与面向文件的编程已没有分别,而这恰恰是 unix 系统一切皆文件思想的又一例证。
什么是套接字的异构性?
如前所述,套接字是对 ISO/OSI 网络协议模型中传输及其以下诸层的逻辑抽象,是对 TCP/IP 网络通信协议的高级封装,因此无论所依赖的是什么硬件,所运行的什么操作系统所使用的是什么编程语言,只要是基于套接字构建的应用程序,只要是在互联网环境中通信,就不会存在任何障碍。
2、通信模式
单播模式
每个数据包发送单个目的主机,目的地址指明单个接收者。
服务器可以及时响应客户机的请求。
服务器可以针对不同客户的不同请求提供个性化的服务。
网络中传输的信息量与请求该信息的用户量成正比,当请求该信息的用户量较大时,网络中将出现多份内容相同的信息流,此时带宽就成了限制传输质量的瓶颈。
广播模式
一台主机向网上的所有其它主机发送数据。
无需路径选择,设备简单,维护方便,成本低廉。
服务器不用向每个客户机单独发送数据,流量负载极低。
无法针对具体客户的具体要求,及时提供个性化的服务。
网络无条件地复制和转发每一台主机产生的信息,所有的主机可以收到所有的信息,而不管是否需要,网络资源利用率低,带宽浪费严重。
禁止广播包穿越路由器,防止在更大范围内泛滥。
多播模式
网络中的主机可以向路由器请求加入或退出某个组,路由器和交换机有选择地复制和转发数据,只将组内数据转发给那些加入组的主机。
需要相同信息的客户机只要加入同一个组即可共享同一份数据,降低了服务器和网络的流量负载。
既能一次将数据传输给多个有需要的主机,又能保证不影响其他不需要的主机。
多播包可以穿越路由器,并在穿越中逐渐衰减。
缺乏纠错机制,丢包错包在所难免。
3、绑定与连接
如前所述,套接字是一个提供给程序员使用的逻辑对象,它表示对 ISO/OSI 网络协议模型中传输层及其以下诸层的的抽象。但真正发送和接收数据的毕竟是那些实实在在的物理设备。这就需要在物理设备和逻辑对象之间建立一种关联,使后续所有针对这个逻辑对象的操作,最终都能反映到实际的物理设备上。建立这种关联关系的过程就叫做绑定。
绑定只是把套接字对象和一个代表自己的物理设备关联起来。但为了实现通信还需要把自己的物理设备与对方的物理设备关联起来。只有这样才能建立一种以物理设备为媒介的,跨越不同进程甚至机器的,多个套接字对象之间的联系。建立这种联系的过程就叫做连接。
二、常用函数
1、函数 socket:创建套接字
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- int socket(int domain, int type, int protocol);
- 返回值:成功返回套接字描述符,失败返回 -1
(1)参数解析
domain:通信域,即地址族,可取以下值:
AF_LOCAL/AF_UNIX 本地通信,即进程间通信 (重点)
AF_INET 基于 IPv4 的网络通信 (重点)
AF_INET6 基于 IPv6 的网络通信
AF_PACKET 基于底层包接口的网络通信
type:套接字类型,可取以下值:
SOCK_STREAM 流式套接字,即使用 TCP 协议的套接字。有序的、可靠的、双向的、面向连接的字节流。
SOCK_DGRAM 数据报套接字,即使用 UDP 协议的套接字。固定长度的、无连接的、不可靠的报文传递。
SOCK_RAW 原始套接字,即使用 IP 协议的套接字。
SOCK_SEQPACKET 固定长度的、有序的、可靠的、面向连接的报文传递。
protocol:特殊协议,通常不用,取 0 即可。表示为给定的域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用 protocol 选择一个特定协议。
下图列出了为因特网域套接字定义的协议:
(2)函数解析
socket 函数所返回的套接字描述符类似于文件描述符,Unix 系统把网络也看成是文件,发送数据即写文件,接收数据即读文件,一切皆文件。
虽然套接字描述符本质上是一个文件描述符,但不是所有参数为文件描述符的函数都可以接受套接字描述符。
下图总结了到目前为止所讨论的大多数以文件描述符为参数的函数使用套接字描述符的行为。未指定和由实现定义的行为通常意味着该函数对套接字描述符无效。例如,lseek 不能以套接字描述符为参数,因为套接字不支持文件偏移量的概念。
(3)示例说明
- int sockfd = socket (AF_LOCAL, SOCK_DGRAN, 0);
- if (sockfd == -1)
- perror ("socket"), exit (1);
- int sockfd = socket (AF_INET, SOCK_STREAM, 0);
- if (sockfd == -1)
- perror ("socket"), exit (1);
- int sockfd = socket (AF_INET, SOCK_DGRAN, 0);
- if (sockfd == -1)
- perror ("socket"), exit (1);
2、函数 shutdown:禁止套接字
套接字通信是双向的。可以采用 shutdown 函数来禁止一个套接字的 I/O。
- #include<sys/socket.h>
- int shutdown(int sockfd, int how);
- 返回值:若成功则返回0,出错则返回-1.
能够关闭(close)套接字,为什么还要使用 shutdown 呢?这里有若干个理由。
首先,只有最后一个活动引用关闭时,close 才释放网络端点。这意味着,如果复制一个套接字(如采用 dup),套接字直到关闭了最后一个引用它的文件描述符之后才会被释放。而 shutdown 允许使一个套接字处于不活动状态,和引用它的文件描述符数目无关。
其次,有时可以很方便地关闭套接字双向传输中的一个方向。
首先,只有最后一个活动引用关闭时,close 才释放网络端点。这意味着,如果复制一个套接字(如采用 dup),套接字直到关闭了最后一个引用它的文件描述符之后才会被释放。而 shutdown 允许使一个套接字处于不活动状态,和引用它的文件描述符数目无关。
其次,有时可以很方便地关闭套接字双向传输中的一个方向。
3、地址结构
套接字接口库通过地址结构定位一个通信主体,可以是一个文件,可以是一台远程主机,也可以是执行自己。
基本地址结构,本身没有实际意义,仅用于泛型化参数。
为使不同格式地址能够传入到套接字函数,地址会被强制转换成一个通用的地址结构 sockaddr
- struct sockaddr{
- sa_family_t sa_family; //地址族
- char sa_data[]; //地址值
- ...
- };
本地地址结构,用于 AF_LOCAL/AF_UNIX 域的本地通信
- #include <sys/un.h>
- struct sockaddr_un
- {
- sa_family_t sun_family; //地址族(AF)LOCAL)
- char sun_path[]; //踏破戒指文件路径
- };
网络地址结构,用于 AF_INET 域的 IPv4 网络通信
- #include <netinet/in.h>
- struct sockaddr_in
- {
- sa_family_t sin_family; //地址族(AF_INET)
- in_port_t sin_port; //端口号
- struct in_addr sin_addr; //IP地址
- };
- struct in_addr
- {
- in_addr_t s_addr;
- };
- typedef unit16_t in_port_t; //无符号短整型
- typedef unit32_t in_addr_t; //无符号长整型
与IPv4因特网域(AF_INET)相比,IPv6因特网域(AF_INET6)套接字使用如下结构sockaddr_in6表示
- struct in6_addr{
- unit8_t s6_addr[16]; //IPv6地址
- }
- struct sockaddr_in6{
- sa_family_t sin6_family; //地址family
- in_port_t sin6_port; //端口地址
- uint32_t sin6_flowinfo; //流量等级和flow信息
- struct in6_addr sin6_addr; //IPv6地址
- uint32_t sin6_scope_id; //set of interfaces for scope
- };
如前所述,通过 IP 地址可以定位网络上的一台主机,但一台主机上可能同时又多个网络应用在运行,究竟想跟哪个网络应用通信呢?这就需要靠所谓的端口号来区分,因为不同的网络应用会使用不同的端口号。用 IP 地址定位主机,再用端口号定位运行子啊这台主机上的一个具体的网络应用,这样一种对通信主体的描述才是唯一确定的。
套接字接口库中的端口号被定义为一个 16 位的无符号整数,其值介于 0 到 65535,其中 0 到 1024 已被系统和一些网络服务占据,比如 21 端口用于 ftp 服务、23端口用于 telnet服务。80端口用于 www 服务等,因此一般应用程序最好选择 1024 以上的端口号,以避免和这些服务冲突。
网络应用与单机应用不同,经常需要在具有不同硬件架构和操作系统的计算机之间交换数据,因此编程语言里一些多字节数据类型的字节序问题就是需要特别予以关注。
这就涉及到大小端问题,参看:C语言再学习-- 大端小端详解(转)
假设一台小端机器里有一个 32 位整数:0x1234 5678,它在内存中按照小端字节序低位低地址的规则存放:
假设一台小端机器里有一个 32 位整数:0x1234 5678,它在内存中按照小端字节序低位低地址的规则存放:
现在,这个整数通过网络被传送到一台大端机器里,内存中的形态不会有丝毫差别,但在大端机器看来地址越低的字节数位应该越高,因此它会把这 4 个字节解读为:0x7856 3412,而这显然有悖于发送端的初衷。
为了避免字节序带来的麻烦,套接字接口库规定凡是在网络中交换的多字节整数(short、int、long、long long 和它们的 unsigned 版本)一律采用网络字节序传输。
而所谓网络字节序,其实就是大端字节序。也就是说,发数据时,先从主机字节序转成网络字节序,然后发送;收数据时,先从网络字节序转成主机字节序,然后使用。
网络地址结构 sockaddr_in 中表示端口号的 sin_port 成员和表示 IP 地址的 sin_addr.s_addr 成员,分别为 2 字节和 4 字节的无符号整数,同样需要用网络字节序来表示。
- struct sockaddr_in add;
- addr.sin_family = AF_INET;
- addr.sin_port = htons (8888);
- addr.sin_addr.s_addr = inet_addr (192.168.182.48);
4、将套接字和地址结构绑定
将套接字对象和自己的地址结构绑定在一起
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 返回值:成功返回 0,失败返回 -1
(1)参数解析
sockfd:套接字描述符
addr:自己的地址结构
addrlen:地址结构长度(以字节为单位)
(2)函数解析
套接字接口库中的很多函数都用到地址结构,但为了同时支持不同的地址结构类型,其参数往往都会选择更一般化的 sockaddr 类型的指针,使用时需要强制类型转换。例如:
- struct sockaddr_un_addr;
- addr.sun_family = AF_LOCAL;
- strcpy (addr.sun+path, "mysock");
- if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- perror ("bind"), exit (1);
- struct sockaddr_in addr;
- addr.sun_family = AF_INET;
- addr.sin_port = htons (8888);
- addr.sin_addr.s_addr = int_addr ("192.168.182.48");
- if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- perror ("bind"), exit (1);
- struct sockaddr_in addr;
- addr.sun_family = AF_INET;
- addr.sin_port = htons (8888);
- addr.sin_addr.s_addr = INADDR_ANY;
- if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- perror ("bind"), exit (1);
查看 /usr/include/netinet/in.h 可看到关于 INADDR_ANY 定义:
#define INADDR_ANY ((in_addr_t) 0x00000000)
(3)扩展
可以调用函数 getsockname 来发现绑定到一个套接字地址。
- #include<sys/socket.h>
- int getsockname(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);
- //成功则返回0,出错则返回-1.
调用 getsockname 之前,设置 alenp 为一个指向整数的指针,该整数指向缓冲区 sockaddr 的大小。返回时,该整数会被设置成返回地址的大小。如果该地址和提供的缓冲区长度不匹配,则将其截断而不报错。如果当前没有绑定到该套接字的地址,其结果没有定义。
如果套接字已经和对方连接,调用 getpeername 来找到对方的地址。
- #include<sys/socket.h>
- int getpeername(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);
- //成功则返回0,出错则返回-1.
5、建立连接
将套接字对象和对方的地址结构连接在一起
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- int connect(int sockfd, const struct sockaddr *addr,
- socklen_t addrlen);
- 返回值:成功返回 0,失败返回 -1<span style="color:#cc6600;">
- </span>
(1)参数解析
sockfd:套接字描述符
addr:对方的地址结构
addlen:地址结构长度(以字节为单位)
(2)函数解析
- struct sockaddr_un addr;
- addr.sun_family = AF_LOCAL;
- strcpy (addr.sun_path, "mysock");
- if (connect (sockfd, (strcut sockaddr*)&addr, sizeof (addr)) == -1)
- perror ("connect"), exit (1);
- struct sockaddr_in addr;
- addr.sun_family = AF_INET;
- addr.sin_port = htons (8888);
- addr.sin_addr.s_addr = inet_addr ("192.168.182.48");
- if (connect (sockfd, (strcut sockaddr*)&addr, sizeof (addr)) == -1)
- perror ("connect"), exit (1);
6、通过套接字发送字节流
- #include <unistd.h>
- ssize_t write(int sockfd, const void *buf, size_t count);
- 返回值:成功返回实际发送的字节数(0 表示未发送),失败返回 -1<span style="color:#cc6600;">
- </span>
(1)参数解析
sockfd:套接字描述符
buf:内存缓冲区
count:期望发送的字节数
(2)函数解析
- ssize_t written = write (sockfd, text, towrite);
- if (written== -1)
- perror ("write"), exit (1);
7、通过套接字接收字节流
- #include <unistd.h>
- ssize_t read(int fd, void *buf, size_t count);
- 返回值:成功返回实际接收的字节数(0表示连接关闭),失败返回 -1
(1)参数解析
sockfd:套接字描述符
buf:内存缓冲区
count:期望读取的字节数
(2)函数解析
- ssize_t readed = read (sockfd, text, toread);
- if (readed == -1)
- perror ("read"), exit (1);
关闭处于打开状态的套接字描述符
sockfd:套接字描述符
(2)函数解析
参看:C语言再学习-- 大端小端详解(转)
将主机或网络字节序的长短整数转换为网络或主机字节序
将主机或网络字节序的长短整数转换为网络或主机字节序
- #include <arpa/inet.h>
- uint32_t htonl(uint32_t hostlong);
- uint16_t htons(uint16_t hostshort);
- uint32_t ntohl(uint32_t netlong);
- uint16_t ntohs(uint16_t netshort);
- 返回值:返回网络或主机字节序的长短整数
htonl() //32位无符号整型的主机字节顺序到网络字节顺序的转换(小端->>大端)
htons() //16位无符号短整型的主机字节顺序到网络字节顺序的转换 (小端->>大端)
ntohl() //32位无符号整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)
ntohs() //16位无符号短整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)
htons() //16位无符号短整型的主机字节顺序到网络字节顺序的转换 (小端->>大端)
ntohl() //32位无符号整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)
ntohs() //16位无符号短整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)
注,
主机字节顺序
,X86一般多为小端(little-endian),
网络字节顺序
,即大端(big-endian);
h表示“主机”字节序,n 表示“网络字节序,l 表示“长”整数,s 表示“短”整数。
举个例子:
- //示例一
- #include <stdio.h>
- #icnlude <arpa/inet.h>
- int main (void)
- {
- union
- {
- short i;
- char a[2];
- }u;
- u.a[0] = 0x11;
- u.a[1] = 0x22;
- printf ("0x%x\n", u.i); //0x2211 为小端 0x1122 为大端
- printf ("0x%.x\n", htons (u.i)); //大小端转换
- return 0;
- }
- 输出结果:
- 0x2211
- 0x1122
10、IP地址转换
(1)点分十进制字符串->网络字节序 32 位无符号整数
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- in_addr_t inet_addr(const char *cp);
- 返回值:返回网络字节序 32 位无符号整数形式的 IP 地址
《1》参数解析
cp:点分十进制字符串形式的 IP 地址
《2》示例说明
- struct sockaddr_in addr;
- addr.sin_addr.s_addr = inet_addr ("1292.168.182.48");
(2)点分十进制字符串->网络字节序 32 位无符号整数
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- int inet_aton(const char *cp, struct in_addr *inp);
- 返回值:成功返回 0,失败返回 -1
《1》参数解析
cp:点分十进制字符串形式的 IP 地址
inp:输出包含网络字节序 32 位无符号整数形式 IP 地址的 in_addr 结构
《2》示例说明
- struct sockaddr_in addr;
- if (inet_aton ("192.168.182.48", &addr.sin_addr) == -1)
- perror ("inet_aton"), exit (1);
(3)网络字节序 32 位无符号整数->点分十进制字符串
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- char *inet_ntoa(struct in_addr in);
- 返回值:返回点分十进制字符串形式的 IP 地址
《1》参数解析
in:包含网络字节序 32 位无符号整数形式 IP 地址的 in_addr 结构
《2》示例说明
- struct sockaddr_in addr;
- if (inet_ntoa ("192.168.182.48", &addr.sin_addr) == -1)
- perror ("inet_ntoa"), exit (1);
- printf ("%s\n", inet_ntoa (addr.sin_addr));
(4)函数 inet_ntop 和 inet_pton
需要说明一下,上面讲的这两个函数 inet_addr 和 inet_ntoa,用于二进制地址格式与点分十进制字符表示(a.c.b.d)之间的相互转换。但是这些函数仅适用于 IPv4 地址。有两个新函数 inet_ntop 和 inet_pton 具有相似的功能,而且同时支持 IPv4 地址和 IPv6 地址。
函数 inet_ntop 将网络字节序的二进制地址转换成文本字符串格式,
inet_pton 将文本字符串格式转换成网络字节序的二进制地址。参数 domian 仅支持两个值:AF_INET 和 AF_INET6。
- #include<arpa/inet.h>
- const char *inet_ntop(int domain, const void *restrict addr, char *restrict str, socklen_t size);
- 返回值:若成功,返回地址字符串指针;若出错,返回NULL。
- int inet_pton(int domain, const char *restrict str, void *restrict addr);
- 返回值:若成功,返回 1;若格式无效,返回 0;若出错,返回 -1
举个例子:
- #include <stdio.h>
- #include <stdlib.h>
- #include <netinet/in.h>
- #include<arpa/inet.h>
- int main(void){
- char addr_p[16];
- struct in_addr addr_n;
- if(inet_pton(AF_INET, "192.168.1.2",&addr_n) < 0){
- perror("inet_pton");
- return -1;
- }
- printf("address:%x\n",addr_n.s_addr);
- if(inet_ntop(AF_INET, &addr_n,addr_p,(socklen_t)sizeof(addr_p)) == NULL){
- perror("inet_ntop");
- return -1;
- }
- printf("address:%s\n",addr_p);
- }
- 输出结果:
- address:201a8c0
- address:192.168.1.2
(1)基于套接字实现进程间通信的编程模型
int sockfd = socket (AF_LOCAL, ...);
准备地址结构时使用 sockaddr_un 结构体类型
准备地址结构时使用 sockaddr_un 结构体类型
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, "mysock");
套接字文件如不再用需要显示删除
套接字文件如不再用需要显示删除
- //服务器 sockA.c
- #include <stdio.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- int main()
- {
- int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
- if (sockfd == -1)
- {
- perror ("socket");
- exit (EXIT_FAILURE);
- }
- struct sockaddr_un addr;
- addr.sun_family = AF_LOCAL;
- strcpy (addr.sun_path, "mysock");
- if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- {
- perror ("bind");
- unlink("mysock");
- exit (EXIT_FAILURE);
- }
- char text[1024];
- ssize_t readed = read (sockfd, text, 1024);
- if (readed == -1)
- {
- perror ("read");
- unlink("mysock");
- exit (EXIT_FAILURE);
- }
- printf("客户机发送内容:%s\n", text);
- if (close (sockfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- unlink("mysock");
- return 0;
- }
- //客户机 sockB.c
- #include <stdio.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- int main()
- {
- int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
- if (sockfd == -1)
- {
- perror ("socket");
- exit (EXIT_FAILURE);
- }
- struct sockaddr_un addr;
- addr.sun_family = AF_LOCAL;
- strcpy (addr.sun_path, "mysock");
- if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- {
- perror ("connect");
- exit (EXIT_FAILURE);
- }
- char text[1024] = "你好,服务器";
- ssize_t written = write (sockfd, text, strlen(text));
- if (written == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- if (close (sockfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
- 输出结果:
- 在一个终端执行:
- # ./sockA
- 客户机发送内容:你好,服务器
- 在另一个终端执行:
- # ./sockB
2、网络通信
(1)基于套接字实现网络通信的编程模型
int sockfd = socket (AF_INET, ...);
准备地址结构时使用 sockaddr_in 结构体类型
准备地址结构时使用 sockaddr_in 结构体类型
—服务器
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
—客户机
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr ("129.168.182.48");
—客户机连接本机服务器可以使用本地环回地址
addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
(3)示例说明
- //服务器 sockNetA.c
- #include <stdio.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <arpa/inet.h>
- int main()
- {
- int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
- if (sockfd == -1)
- {
- perror ("socket");
- exit (EXIT_FAILURE);
- }
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons (8888);
- addr.sin_addr.s_addr = INADDR_ANY;
- if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- {
- perror ("bind");
- exit (EXIT_FAILURE);
- }
- char text[1024];
- ssize_t readed = read (sockfd, text, 1024);
- if (readed == -1)
- {
- perror ("read");
- exit (EXIT_FAILURE);
- }
- printf("客户机发送内容:%s\n", text);
- if (close (sockfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
- //客户机 sockNetB.c
- #include <stdio.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <arpa/inet.h>
- int main()
- {
- int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
- if (sockfd == -1)
- {
- perror ("socket");
- exit (EXIT_FAILURE);
- }
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons (8888);
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
- {
- perror ("connect");
- exit (EXIT_FAILURE);
- }
- char text[1024] = "你好,服务器";
- ssize_t written = write (sockfd, text, strlen(text));
- if (written == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- if (close (sockfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
- 输出结果:
- 在一个终端执行:
- # ./sockNetA
- 客户机发送内容:你好,服务器
- 在另一个终端执行:
- # ./sockNetB