宁波做网站seo的,免费企业网站模板下载,如何做地方网站,商标logo设计公司目录 一、对于Socket、TCP/UDP、端口号的认知#xff1a;
1.1 什么是Socket#xff1a;
1.2 TCP/UDP对比#xff1a;
1.3 端口号的作用#xff1a;
二、字节序
2.1 字节序相关概念#xff1a;
2.2 为什么会有字节序#xff1a;
2.3 主机字节序转换成网络字节序函数…目录 一、对于Socket、TCP/UDP、端口号的认知
1.1 什么是Socket
1.2 TCP/UDP对比
1.3 端口号的作用
二、字节序
2.1 字节序相关概念
2.2 为什么会有字节序
2.3 主机字节序转换成网络字节序函数原型和头文件
2.4 网络字节序转换成主机字节序函数原型和头文件
三、socket服务器和客户端开发步骤
3.1 TCP通信流程
3.2 UDP通信流程
四、socket 相关函数
4.1 创建套接字函数socket()原型和头文件
4.2 绑定套接字函数bind()原型和头文件
4.3 字符串格式的IP地址转换成网络格式函数inet_aton()原型和头文件
4.4 网络格式的IP地址转换成字符串格式函数inet_ntoa()原型和头文件
4.5 监听被绑定的端口函数listen()原型和头文件
4.6 接收客户端连接请求函数accept()原型和头文件
4.7 客户端发送连接请求函数connect()原型和头文件
4.8 TCP发送信息函数send()原型和头文件
4.9 TCP接收信息函数recv()原型和头文件
五、实现客户端服务器通信
5.1 实现客户端和服务器双方聊天
5.2 实现多个客户端接入服务器通信 一、对于Socket、TCP/UDP、端口号的认知
1.1 什么是Socket 所谓套接字Socket就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲套接字上联应用进程下联网络协议栈是应用程序通过网络协议进行通信的接口是应用程序与网络协议栈进行交互的接口。 套接字是通信的基石是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限但这时一定要执行某种解释程序)各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。 Socket套接字可以看成是两个网络应用程序进行通信时各自通信连接中的端点这是一个逻辑上的概念。它是网络环境中进程间通信的API也是可以被命名和寻址的通信端点使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中该 Socket通过与网络接口卡NIC相连的传输介质将这段信息送到另外一台主机的 Socket中使对方能够接收到这段信息。 Socket是由IP地址和端口结合的提供向应用层进程传送数据包的机制。 Socket起源于Unix而Unix/Linux基本哲学之一就是“一切皆文件”都可以用“打开open – 读写write/read – 关闭close”模式来操作。Socket可以看作该模式的一个实现Socket即是一种特殊的文件一些Socket函数就是对其进行的操作读/写IO、打开、关闭。 socket其实就是一根通信电缆两端的电话终端电话接通后就相当两个socket建立了连接两个电话之间可以相互通话两个socket之间就可以实时收发数据socket仅仅是一个通信工具通信工具通信工具重要的事说三遍OSI模型中的第四层传输层的API接口这一层通常使用两种协议TCP或UDP来传输并不是一种协议。TCP、UDP、HTTP才是我们通常理解的协议。 也就是说Socket这个工具一般使用TCP和UDP两种协议来通信否则光杆socket并没有毛用。其实我们所认识到的互联网中的各种通信web请求、即时通讯、文件传输和共享等等底层都是通过Socket工具来实现的所以说互联网一切皆socket。搞懂了socket你就相当于打通了任督二脉。 在UNIX、Linux系统中为了统一对各种硬件的操作简化接口不同的硬件设备也都被看成一个文件。对这些文件的操作等同于对磁盘上普通文件的操作。 为了表示和区分已经打开的文件UNIX/Linux会为每个文件分配一个ID这个文件就是一个整数被称为文件描述符 例如 通常用 0 来表示标准输入文件stdin它对应的硬件设备就是键盘 通常用 1 来表示标准输出文件stdout它对应的硬件设备就是显示器。 网络连接也是一个文件它也有文件描述符 我们可以通过 socket() 函数来创建一个网络连接或者说打开一个网络文件socket() 的返回值就是文件描述符注意在windows下的socket返回的叫文件句柄并不是叫文件描述符。有了文件描述符我们就可以使用普通的文件操作函数来传输数据了例如 用 read() 读取从远程计算机传来的数据 用 write() 向远程计算机写入数据。
1.2 TCP/UDP对比 TCP面向连接如打电话要先拨号建立连接UDP是无连接的即发送数据前不需要建立连接。 TCP提供可靠的服务也就是说通过TCP连接传送的数据是无差错不丢失不重复且按序到达UDP是尽最大努力交付即保证可靠交付。 TCP是面向字节流实际上是TCP把数据看成是一连串无结构的字节流UDP是面向报文的UDP没有拥塞控制因此网络出现拥塞不会是源主机的发送速率降低对实时应用很有用如IP电话实时视频会议…。 每一条TCP连接只能是点到点的UDP支持一对一一对多多对一和多对多的交互通信。 TCP的首部开销20字节UDP的首部开销小只有8个字节。 TCP是逻辑通信信道是全双工的可靠信道UDP是不可靠信道。
1.3 端口号的作用
一台拥有 IP 地址的主机可以提供许多服务比如 Web 服务、FTP 服务、 SMTP 服务等。 这些服务完全可以通过 1 个 IP 地址来实现。那么主机是怎样区分不同的网络服务呢显然不能只靠 IP 地址因为 IP 地址与网络服务的关系是一对多的关系。 实际上是通过“ IP 地址 端口号”来区分不同的服务的。端口提供了一种访问通道服务器一般都是通过知名端口号来识别的。例如对于每个 TCP/IP 实现来说FTP服务器的 TCP 端口号都是 21 每个 Telnet 服务器的 TCP端口号都是 23 每个 TFTP( 简单文件传送协议 ) 服务器的 UDP 端口号都是 69 。
端口号用两个字节表示的整数它的取值范围是065535。其中0~1023之间的端口号用于一些知名的网络服务和应用普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用会导致当前程序启动失败。如果IP地址是相当于一栋楼的楼号的话那么端口号就相当于是这栋楼里面的房间的房号
利用 协议 IP地址 端口号 三元组合就可以标识网络中的进程了那么进程间的通信就可以利用这个标识与其它进程进行交互。
二、字节序
2.1 字节序相关概念 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。在设计计算机系统的时候有两种处理内存中数据的方法即大端字节序大端格式、小端字节序小端格式 小段字节序Little endian将低序字节存储在起始地址 大端字节序Big endian 将高序字节存储在起始地址 内存地址小段字节序Little endian大端字节序Big endian40000x040x0140010x030x0240020x020x0340030x010x04
2.2 为什么会有字节序 计算机电路先处理低位字节效率比较高因为计算都是从低位开始的。所以计算机的内部处理都是小端字节序。但是人类还是习惯读写大端字节序。所以除了计算机的内部处理其他的场合几乎都是大端字节序比如网络传输和文件储存。 计算机处理字节序的时候不知道什么是高位字节什么是低位字节。它只知道按顺序读取字节先读第一个字节再读第二个字节。如果是大端字节序先读到的就是高位字节后读到的就是低位字节。小端字节序正好相反。只有读取的时候才必须区分字节序其他情况都不用考虑。
2.3 主机字节序转换成网络字节序函数原型和头文件
#include arpa/inet.h // 包含对网络地址的操作函数的头文件uint16_t htons(uint16_t hostshort); //将16位主机字节序数据转换成网络字节序数据
uint32_t htonl(uint32_t hostlong); //将32位主机字节序数据转换成网络字节序数据uint16_t 函数返回值成功返回网络字节序的值
uint16_t hostshort 需要转换的16位主机字节序数据uint16_tunsigned short intuint32_t 函数返回值成功返回网络字节序的值
uint32_t hostlong 需要转换的32位主机字节序数据uint32_t32位无符号整型
2.4 网络字节序转换成主机字节序函数原型和头文件
#include arpa/inet.h // 包含对网络地址的操作函数的头文件uint16_t ntohs(uint16_t netshort); //将32位网络字节序数据转换成主机字节序数据
uint32_t ntohl(uint32_t netlong); //将16位网络字节序数据转换成主机字节序数据uint16_t 函数返回值返回主机字节序的值
uint32_t netlong 需要转换的16位网络字节序数据uint16_tunsigned short intuint32_t 函数返回值返回主机字节序的值
uint32_t netlong 需要转换的32位网络字节序数据uint32_tunsigned int
三、socket服务器和客户端开发步骤
3.1 TCP通信流程 TCP传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议即传输数据之前在发送端和接收端建立逻辑连接然后再传输数据它提供了两台计算机之间可靠无差错的数据传输。 服务器Server 创建套接字socket 将socket与IP地址和端口绑定bind 监听被绑定的端口listen 接收连接请求accept 从socket中读取客户端发送来的信息read 向socket中写入信息write 关闭socketclose 客户端Client 创建套接字socket 连接指定计算机的端口connect 向socket中写入信息write 从socket中读取服务端发送过来的消息read 关闭socketclose
3.2 UDP通信流程
用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时不需要建立连接不管对方端服务是否启动直接将数据、数据源和目的地都封装在数据包中直接发送。每个数据包的大小限制在64k以内。它是不可靠协议因为无连接所以传输速度快但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。 服务器Server 使用函数socket()生成套接字文件描述符 通过struct sockaddr_in 结构设置服务器地址和监听端口 使用bind() 函数绑定监听端口将套接字文件描述符和地址类型变量struct sockaddr_in 进行绑定 接收客户端的数据使用recvfrom() 函数接收客户端的网络数据 向客户端发送数据使用sendto() 函数向服务器主机发送数据 关闭套接字使用close() 函数释放资源 客户端Client 使用socket()生成套接字文件描述符 通过struct sockaddr_in 结构设置服务器地址和监听端口 向服务器发送数据sendto() 接收服务器的数据recvfrom() 关闭套接字close()
四、socket 相关函数
4.1 创建套接字函数socket()原型和头文件
/*Linux下 man 2 socket查看手册
*/
#include sys/types.h
#include sys/socket.hint socket(int domain, int type, int protocol);int 函数返回值成功返回非负套接字描述符失败返回-1int domain 指明所使用的协议族通常为AF_INET表示互联网协议族TCP/IP协议族1. AF_INET IPv4因特网域2. AF_INET6 IPv6因特网域3. AF_UNIX Unix域4. AF_ROUTE 路由套接字5. AF_KEY 密钥套接字6. AF_UNSPEC 未指定int type 参数设定socket的类型1. SOCK_STREAM:流式套接字提供可靠的面向连接的通信流它使用TCP协议从而保证了数据传输的正确性和顺序性2. SOCK_DGRAM数据报套接字定义了一种无连接的服数据通过相互独立的报文进行传输是无序的并且不保证是可靠无差错的。它使用 数据报协议UDP3. SOCK_RAW:允许程序使用底层协议原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便主要用于一些协议 的开发。int protocol 通常赋值为“0”0选择type对应的默认协议1. IPPROTO_TCP TCP传输协议2. IPPROTO_UDP UDP传输协议3. IPPROTO_SCTP SCTP传输协议4. IPPROTO_TIPC TIPC传输协议/*函数说明用于创建套接字同时指定协议和类型*/
4.2 绑定套接字函数bind()原型和头文件
/*Linux下 man 2 bind查看手册
*/
#include sys/types.h
#include sys/socket.hint bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int 函数返回值如果成功则返回0如果失败则返回-1int sockfd 是一个socket描述符struct sockaddr *addr 是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针指向要绑定给sockfd的协议地址结构这 个地址结构根据地址创建socket时的地址协议族的不同而不同。
struct sockaddr {sa_family_t sa_family;//协议族char sa_data[14];//IP端口号
}
说明sockaddr在头文件#include sys/socket.h中定义sockaddr的缺陷是sa_data把目标地址和端口信息混在一起了struct sockaddr_in {//如何找到这个结构体在下方有详解__kernel_sa_family_t sin_family; //协议族__be16 sin_port; //端口号 struct in_addr sin_addr; //IP地址结构体 unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];/*填充 没有实际意义只是为跟sockaddr结构在内存在内存中对其这样两者才能相互*/
};
/* Internet address. */
struct in_addr
{uint32_t s_addr; /* address in network byte order */
};
说明sockaddr_in在头文件#includenetinet/in.h或#include arpa/inet.h中定义该结构体解决了sockaddr的缺陷把port和addr 分开储存在两个变量中上述两者结构体长度一样都是16个字节即占用的内存大小是一致的因此可以互相转化。二者是并列结构指向sockaddr_in结构的指针也可以指向sockaddr。
一般先把sockaddr_in变量赋值后强制类型转换后传入用sockaddr做参数的函数sockaddr_in用于socket定义和赋值sockaddr用于函数参数。socklen_t addrlen 地址的长度通常用sizeof(struct sockaddr_in)表示;/*函数说明用于绑定IP地址和端口号到 socket*/
4.3 字符串格式的IP地址转换成网络格式函数inet_aton()原型和头文件
/*Linux下 man inet_aton查看手册
*/
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hint inet_aton(const char *cp, struct in_addr *inp);const char *cp 你的IP地址
struct in_addr *inp 存放你这个IP地址指针结构体在上面bind中有这个结构体例如s_addr/*函数说明把字符串形式的IP地址如192.168.1.123装换为网络能识别的格式*/
4.4 网络格式的IP地址转换成字符串格式函数inet_ntoa()原型和头文件
/*Linux下 man inet_ntoa查看手册
*/
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hchar *inet_ntoa(struct in_addr inaddr);struct in_addr inaddr 存放网络格式IP地址的结构体在上面bind中有这个结构体/*函数说明把网络格式的IP地址转换成字符串形式*/
4.5 监听被绑定的端口函数listen()原型和头文件
/*Linux下 man 2 listen查看手册
*/
#include sys/types.h
#include sys/socket.hint listen(int sockfd, int backlog);int 函数返回值如果成功则返回0如果失败则返回-1int sockfd socket系统调用返回的服务端socket描述符
int backlog 指定在请求队列中允许的最大的请求数大多数系统默认为5
函数功能 设置能处理的最大连接数listen并未开始接受连线只是设置了socket的listen模式listen函数只用于服务器端服务器进程不知道要与谁进行连接因此它不会主动的要求与某个进程连接只是一直监听是否有其他客户进程与之连接然后响应该连接请求并对它做出处理一个服务进程可以同时处理多个客户进程的连接主要就连个功能将一个未连接的套接字转换为一个被动套接字监听规定内核为相应套接字排队的最大连接数。 内核为任何一个给定监听套接字维护两个队列 未完成连接队列每个这样的SYN报文段对应其中一项已由某个客户端发出并到达服务器而服务器正在等待完成相应的TCP三次握手过程这些套接字处于SYN_REVD状态 已完成连接队列每个已完成TCP三次握手过程的客户端对应其中一项这些套接字处于ESTABLISHED状态
4.6 接收客户端连接请求函数accept()原型和头文件
/*Linux下 man 2 accept查看手册
*/
#include sys/types.h /* See NOTES */
#include sys/socket.hint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int 函数返回值这些系统调用返回被接受套接字的文件描述符(一个非负整数)。如果出现错误则返回-1该函数的返回值是一个新的套接字的描述符返回值是表示已连接的套接字描述符而第一个参数是服务器监听套接字描述符一个服务器通常仅仅创建一个监听套接字它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字表示TCP三次握手已完成当服务器完成对某个给定客户的服务时相应的已连接套接字就会被关闭。int sockfd 是socket系统调用返回的服务器端socket描述符
struct sockaddr *addr 用来返回已连接的对端客户端的协议地址socklen_t *addrlen 客户端地址长度,注意需要取地址/*
函数说明accept函数由TCP服务器调用用于从已完成连接队列对头返回下一个已完成连接如果已完成连接队列为空那么进程被投入睡眠。
*/
4.7 客户端发送连接请求函数connect()原型和头文件
/*Linux下 man 2 connect查看手册
*/
#include sys/types.h
#include sys/socket.hint connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int 函数返回值如果连接或者绑定成功则返回0如果失败则返回-1int sockfd 客户端创建的socket描述符
struct sockaddr *addr 是服务器端的IP地址和端口号的地址结构指针
socklen_t addrlen 地址的长度通常被设置为 sizeof(struct sockaddr)/*函数说明该函数用于绑定之后的client端客户端与服务器建立连接*/
4.8 TCP发送信息函数send()原型和头文件
/*Linux下 man 2 send查看手册
*/
#include sys/types.h
#include sys/socket.hssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t 函数返回值如果成功则返回实际发送的字节数如果失败则返回-1int sockfd 为已建立好连接的套接字描述符即accept函数的返回值
void *buf 要发送的内容
size_t len 发送内容的长度
int flags 设置为MSG_DONTWAITMSG 时 表示非阻塞设置为0时 功能和write一样/*函数说明函数只能对处于连接状态的套接字进行使用参数sockfd为已建立好连接的套接字描述符*/
4.9 TCP接收信息函数recv()原型和头文件
/*Linux下 man 2 recv查看手册
*/
#include sys/types.h
#include sys/socket.hssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t 函数返回值如果成功则返回实际发送的字节数失败则返回-1int sockfd 在哪个套接字接收
void *buf 存放要接收的数据的首地址
size_t len 要接收的数据的字节大小
int flags 设置为MSG_DONTWAITMSG 时 表示非阻塞设置为0时 功能和read一样/*函数说明接收套接字中的数据*/
五、实现客户端服务器通信
5.1 实现客户端和服务器双方聊天
/*server1.c*/
#include stdio.h // 包含标准输入输出头文件
#include sys/types.h // 包含系统数据类型头文件
#include sys/socket.h // 包含系统套接字库的头文件
#include stdlib.h // 包含标准库头文件
#include arpa/inet.h // 包含网络地址转换头文件
#include netinet/in.h // 包含IPv4地址头文件
#include string.h // 包含字符串头文件
#include unistd.h // 包含unistd.h头文件int main(int argc, char **argv)
{int s_fd; // 套接字文件描述符int c_fd; // 客户端套接字文件描述符int n_read; // 读入字节数int n_write; // 写出字节数char readBuf[128] {0}; // 读入缓冲区char writeBuf[128] {0}; // 写出缓冲区 struct sockaddr_in server_addr; // 服务器地址结构体struct sockaddr_in client_addr; // 客户端地址结构体memset(server_addr, 0, sizeof(server_addr)); // 服务器地址结构体清零memset(client_addr, 0, sizeof(client_addr)); // 客户端地址结构体清零if(argc ! 3){ // 参数检查 printf(参数错误请按照格式输入./server IP地址 端口号\n);exit(-1);}//int socket(int domain, int type, int protocol);s_fd socket(AF_INET, SOCK_STREAM, 0); // 创建TCP/IP套接字if(s_fd -1){printf(创建套接字失败\n);perror(socket); // 输出错误信息exit(-1);}server_addr.sin_family AF_INET; // 设置服务器地址族为IPv4server_addr.sin_port htons(atoi(argv[2])); // 设置服务器端口号//inet_aton(127.0.0.1, server_addr.sin_addr); inet_aton(argv[1], server_addr.sin_addr); // 设置服务器IP地址//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int ret bind(s_fd,(struct sockaddr *)server_addr,sizeof(server_addr)); // 绑定服务器地址if(ret -1){printf(绑定服务器地址失败\n);perror(bind); // 输出错误信息exit(-1);}//int listen(int sockfd, int backlog);ret listen(s_fd, 10); // 监听套接字if(ret -1){printf(监听套接字失败\n);perror(listen); // 输出错误信息exit(-1);}printf(服务器启动成功\n);int client_addr_len sizeof(client_addr); // 客户端地址长度//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);while(1){printf(等待客户端连接...\n);c_fd accept(s_fd, (struct sockaddr *)client_addr, client_addr_len); // 接受客户端连接请求if(c_fd -1){printf(接受客户端连接请求失败\n);}printf(客户端连接成功\n); // 输出客户端地址printf(客户端地址%s:%d\n, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));if(fork() 0){ // 子进程处理客户端请求if(fork() 0){ // 孙子进程处理客户端请求 while(1){memset(writeBuf, 0, sizeof(writeBuf)); // 写出缓冲区清零printf(请输入要发送的数据\n); // 输出提示信息 fgets(writeBuf, sizeof(writeBuf), stdin); // 读入用户输入 //ssize_t write(int fd, const void *buf, size_t count);n_write write(c_fd, writeBuf, sizeof(writeBuf)); // 写出数据if(n_write -1){printf(发送数据失败\n);}else{printf(发送数据%s,发送字节数%d\n, writeBuf, n_write); // 输出发送的数据}}}while(1){memset(readBuf, 0, sizeof(readBuf)); // 读入缓冲区清零//ssize_t read(int fd, void *buf, size_t count);n_read read(c_fd, readBuf, sizeof(readBuf)); // 读入数据if(n_read -1){printf(读取数据失败\n);perror(read); // 输出错误信息}else{printf(接收字节数%d\n,n_read); // 输出接收到的数据printf(接收数据%s\n, readBuf); // 输出接收到的数据}}break; // 子进程退出}}return 0;
}
/*client.c*/
#include stdio.h //包含标准输入输出头文件
#include sys/types.h //包含系统数据类型头文件
#include sys/socket.h //包含套接字头文件
#include netinet/in.h //包含IPv4头文件
#include arpa/inet.h //包含网络地址转换头文件
#include stdlib.h //包含标准库头文件
#include string.h //包含字符串头文件
#include unistd.h //包含unistd头文件
#include errno.h //包含错误号头文件 int main(int argc, char **argv)
{int c_fd; //客户端套接字文件描述符int n_write; //写入字节数int n_read; // 读入字节数 char sendBuf[128] {0}; //发送数据缓冲区char readBuf[128]; // 读入数据缓冲区 struct sockaddr_in client_addr; //客户端地址结构体memset(client_addr, 0, sizeof(client_addr)); //客户端地址结构体清零if(argc ! 3){ //参数个数不正确printf(参数错误请按照格式输入./client IP地址 端口号\n);exit(-1);}//int socket(int domain, int type, int protocol);c_fd socket(AF_INET, SOCK_STREAM, 0); //创建TCP/IP套接字if(c_fd -1){printf(创建套接字失败\n); perror(socket);exit(-1);}client_addr.sin_family AF_INET; // 设置客户端地址族为IPv4client_addr.sin_port htons(atoi(argv[2])); //设置客户端端口号inet_aton(argv[1], client_addr.sin_addr);; //设置客户端IP地址//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int ret connect(c_fd, (struct sockaddr *)client_addr, sizeof(struct sockaddr)); //连接服务器if(ret -1){printf(连接服务器失败\n);perror(connect);exit(-1);}printf(连接服务器成功\n);while(1){if(fork() 0){ //子进程发送数据 while(1){memset(sendBuf, 0, sizeof(sendBuf)); //清空发送数据缓冲区printf(请输入要发送的数据\n);fgets(sendBuf, sizeof(sendBuf), stdin); //从控制台获取输入数据//ssize_t write(int fd, const void *buf, size_t count);n_write write(c_fd, sendBuf, sizeof(sendBuf)); //发送数据if(n_write -1){printf(发送数据失败\n);}else{printf(发送数据成功共发送%d字节数据\n, n_write);printf(发送的数据为%s\n, sendBuf);}}}while(1){memset(readBuf, 0, sizeof(readBuf)); //清空读入数据缓冲区// ssize_t read(int fd, void *buf, size_t count);n_read read(c_fd, readBuf, sizeof(readBuf)); //接收数据if(n_read -1){printf(接收数据失败\n);}else{printf(接收数据成功共接收%d字节数据\n, n_read);printf(接收到的数据为%s\n, readBuf);}}}// close(c_fd); //关闭套接字 return 0;
} 5.2 实现多个客户端接入服务器通信
/*server2.c*/
#include stdio.h // 包含标准输入输出头文件
#include sys/types.h // 包含系统数据类型头文件
#include sys/socket.h // 包含系统套接字库的头文件
#include stdlib.h // 包含标准库头文件
#include arpa/inet.h // 包含网络地址转换头文件
#include netinet/in.h // 包含IPv4地址头文件
#include string.h // 包含字符串头文件
#include unistd.h // 包含unistd.h头文件int main(int argc, char **argv)
{int s_fd; // 套接字文件描述符int c_fd; // 客户端套接字文件描述符int n_read; // 读入字节数int n_write; // 写出字节数int mark 0; // 标记char readBuf[128] {0}; // 读入缓冲区char writeBuf[128] {0}; // 写出缓冲区 struct sockaddr_in server_addr; // 服务器地址结构体struct sockaddr_in client_addr; // 客户端地址结构体memset(server_addr, 0, sizeof(server_addr)); // 服务器地址结构体清零memset(client_addr, 0, sizeof(client_addr)); // 客户端地址结构体清零if(argc ! 3){ // 参数检查 printf(参数错误请按照格式输入./server IP地址 端口号\n);exit(-1);}//int socket(int domain, int type, int protocol);s_fd socket(AF_INET, SOCK_STREAM, 0); // 创建TCP/IP套接字if(s_fd -1){printf(创建套接字失败\n);perror(socket); // 输出错误信息exit(-1);}server_addr.sin_family AF_INET; // 设置服务器地址族为IPv4server_addr.sin_port htons(atoi(argv[2])); // 设置服务器端口号//inet_aton(127.0.0.1, server_addr.sin_addr); inet_aton(argv[1], server_addr.sin_addr); // 设置服务器IP地址//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int ret bind(s_fd,(struct sockaddr *)server_addr,sizeof(server_addr)); // 绑定服务器地址if(ret -1){printf(绑定服务器地址失败\n);perror(bind); // 输出错误信息exit(-1);}//int listen(int sockfd, int backlog);ret listen(s_fd, 10); // 监听套接字if(ret -1){printf(监听套接字失败\n);perror(listen); // 输出错误信息exit(-1);}printf(服务器启动成功\n);int client_addr_len sizeof(client_addr); // 客户端地址长度//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);while(1){printf(等待客户端连接...\n);c_fd accept(s_fd, (struct sockaddr *)client_addr, client_addr_len); // 接受客户端连接请求if(c_fd -1){printf(接受客户端连接请求失败\n);}printf(客户端连接成功\n); // 输出客户端地址printf(客户端地址%s:%d\n, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));mark; // 标记 if(fork() 0){ // 子进程处理客户端请求if(fork() 0){ // 孙子进程处理客户端请求 while(1){sprintf(writeBuf, Welcome NO.%dclient\n, mark); // 构造数据//ssize_t write(int fd, const void *buf, size_t count);n_write write(c_fd, writeBuf, sizeof(writeBuf)); // 写出数据if(n_write -1){printf(发送数据失败\n);}else{printf(发送数据%s,发送字节数%d\n, writeBuf, n_write); // 输出发送的数据}sleep(3); // 休眠3秒 }}while(1){memset(readBuf, 0, sizeof(readBuf)); // 读入缓冲区清零//ssize_t read(int fd, void *buf, size_t count);n_read read(c_fd, readBuf, sizeof(readBuf)); // 读入数据if(n_read -1){printf(读取数据失败\n);perror(read); // 输出错误信息}else{printf(接收字节数%d\n,n_read); // 输出接收到的数据printf(接收数据%s\n, readBuf); // 输出接收到的数据}}break; // 子进程退出}}return 0;
}
/*client.c*/
#include stdio.h //包含标准输入输出头文件
#include sys/types.h //包含系统数据类型头文件
#include sys/socket.h //包含套接字头文件
#include netinet/in.h //包含IPv4头文件
#include arpa/inet.h //包含网络地址转换头文件
#include stdlib.h //包含标准库头文件
#include string.h //包含字符串头文件
#include unistd.h //包含unistd头文件
#include errno.h //包含错误号头文件 int main(int argc, char **argv)
{int c_fd; //客户端套接字文件描述符int n_write; //写入字节数int n_read; // 读入字节数 char sendBuf[128] {0}; //发送数据缓冲区char readBuf[128]; // 读入数据缓冲区 struct sockaddr_in client_addr; //客户端地址结构体memset(client_addr, 0, sizeof(client_addr)); //客户端地址结构体清零if(argc ! 3){ //参数个数不正确printf(参数错误请按照格式输入./client IP地址 端口号\n);exit(-1);}//int socket(int domain, int type, int protocol);c_fd socket(AF_INET, SOCK_STREAM, 0); //创建TCP/IP套接字if(c_fd -1){printf(创建套接字失败\n); perror(socket);exit(-1);}client_addr.sin_family AF_INET; // 设置客户端地址族为IPv4client_addr.sin_port htons(atoi(argv[2])); //设置客户端端口号inet_aton(argv[1], client_addr.sin_addr);; //设置客户端IP地址//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int ret connect(c_fd, (struct sockaddr *)client_addr, sizeof(struct sockaddr)); //连接服务器if(ret -1){printf(连接服务器失败\n);perror(connect);exit(-1);}printf(连接服务器成功\n);while(1){if(fork() 0){ //子进程发送数据 while(1){memset(sendBuf, 0, sizeof(sendBuf)); //清空发送数据缓冲区printf(请输入要发送的数据\n);fgets(sendBuf, sizeof(sendBuf), stdin); //从控制台获取输入数据//ssize_t write(int fd, const void *buf, size_t count);n_write write(c_fd, sendBuf, sizeof(sendBuf)); //发送数据if(n_write -1){printf(发送数据失败\n);}else{printf(发送数据成功共发送%d字节数据\n, n_write);printf(发送的数据为%s\n, sendBuf);}}}while(1){memset(readBuf, 0, sizeof(readBuf)); //清空读入数据缓冲区// ssize_t read(int fd, void *buf, size_t count);n_read read(c_fd, readBuf, sizeof(readBuf)); //接收数据if(n_read -1){printf(接收数据失败\n);}else{printf(接收数据成功共接收%d字节数据\n, n_read);printf(接收到的数据为%s\n, readBuf);}}}// close(c_fd); //关闭套接字 return 0;
}
文章转载自: http://www.morning.ctbr.cn.gov.cn.ctbr.cn http://www.morning.yrblz.cn.gov.cn.yrblz.cn http://www.morning.jrbyz.cn.gov.cn.jrbyz.cn http://www.morning.prjty.cn.gov.cn.prjty.cn http://www.morning.sfwfk.cn.gov.cn.sfwfk.cn http://www.morning.prkdl.cn.gov.cn.prkdl.cn http://www.morning.rkxk.cn.gov.cn.rkxk.cn http://www.morning.mfsjn.cn.gov.cn.mfsjn.cn http://www.morning.smrty.cn.gov.cn.smrty.cn http://www.morning.rqfzp.cn.gov.cn.rqfzp.cn http://www.morning.rkfxc.cn.gov.cn.rkfxc.cn http://www.morning.rykmf.cn.gov.cn.rykmf.cn http://www.morning.wbxtx.cn.gov.cn.wbxtx.cn http://www.morning.dsgdt.cn.gov.cn.dsgdt.cn http://www.morning.thntp.cn.gov.cn.thntp.cn http://www.morning.tqqfj.cn.gov.cn.tqqfj.cn http://www.morning.mnslh.cn.gov.cn.mnslh.cn http://www.morning.gwkjg.cn.gov.cn.gwkjg.cn http://www.morning.xrpwk.cn.gov.cn.xrpwk.cn http://www.morning.pfgln.cn.gov.cn.pfgln.cn http://www.morning.fydsr.cn.gov.cn.fydsr.cn http://www.morning.qrmyd.cn.gov.cn.qrmyd.cn http://www.morning.mpnff.cn.gov.cn.mpnff.cn http://www.morning.brqjs.cn.gov.cn.brqjs.cn http://www.morning.prjns.cn.gov.cn.prjns.cn http://www.morning.jjzrh.cn.gov.cn.jjzrh.cn http://www.morning.ykwqz.cn.gov.cn.ykwqz.cn http://www.morning.trhrk.cn.gov.cn.trhrk.cn http://www.morning.bnxnq.cn.gov.cn.bnxnq.cn http://www.morning.hyhzt.cn.gov.cn.hyhzt.cn http://www.morning.qqbw.cn.gov.cn.qqbw.cn http://www.morning.rwfp.cn.gov.cn.rwfp.cn http://www.morning.sgnxl.cn.gov.cn.sgnxl.cn http://www.morning.xnnpy.cn.gov.cn.xnnpy.cn http://www.morning.lpcpb.cn.gov.cn.lpcpb.cn http://www.morning.msgrq.cn.gov.cn.msgrq.cn http://www.morning.pumali.com.gov.cn.pumali.com http://www.morning.fysdt.cn.gov.cn.fysdt.cn http://www.morning.slqzb.cn.gov.cn.slqzb.cn http://www.morning.kqlrl.cn.gov.cn.kqlrl.cn http://www.morning.qcsbs.cn.gov.cn.qcsbs.cn http://www.morning.kpxzq.cn.gov.cn.kpxzq.cn http://www.morning.zbqsg.cn.gov.cn.zbqsg.cn http://www.morning.ndcf.cn.gov.cn.ndcf.cn http://www.morning.xfncq.cn.gov.cn.xfncq.cn http://www.morning.dyxlj.cn.gov.cn.dyxlj.cn http://www.morning.jtybl.cn.gov.cn.jtybl.cn http://www.morning.nzwp.cn.gov.cn.nzwp.cn http://www.morning.xqcst.cn.gov.cn.xqcst.cn http://www.morning.frpfk.cn.gov.cn.frpfk.cn http://www.morning.hrjrt.cn.gov.cn.hrjrt.cn http://www.morning.mmxt.cn.gov.cn.mmxt.cn http://www.morning.rlhgx.cn.gov.cn.rlhgx.cn http://www.morning.xyhql.cn.gov.cn.xyhql.cn http://www.morning.nqlnd.cn.gov.cn.nqlnd.cn http://www.morning.dfhkh.cn.gov.cn.dfhkh.cn http://www.morning.rbtny.cn.gov.cn.rbtny.cn http://www.morning.hpdpp.cn.gov.cn.hpdpp.cn http://www.morning.qscsy.cn.gov.cn.qscsy.cn http://www.morning.tjkth.cn.gov.cn.tjkth.cn http://www.morning.rdnkx.cn.gov.cn.rdnkx.cn http://www.morning.wdprz.cn.gov.cn.wdprz.cn http://www.morning.qzqjz.cn.gov.cn.qzqjz.cn http://www.morning.dmsxd.cn.gov.cn.dmsxd.cn http://www.morning.gwzfj.cn.gov.cn.gwzfj.cn http://www.morning.zdzgf.cn.gov.cn.zdzgf.cn http://www.morning.slnz.cn.gov.cn.slnz.cn http://www.morning.nrftd.cn.gov.cn.nrftd.cn http://www.morning.dtzxf.cn.gov.cn.dtzxf.cn http://www.morning.bsgfl.cn.gov.cn.bsgfl.cn http://www.morning.fjshyc.com.gov.cn.fjshyc.com http://www.morning.qqhfc.cn.gov.cn.qqhfc.cn http://www.morning.hcsqznn.cn.gov.cn.hcsqznn.cn http://www.morning.wklmj.cn.gov.cn.wklmj.cn http://www.morning.coatingonline.com.cn.gov.cn.coatingonline.com.cn http://www.morning.ghpld.cn.gov.cn.ghpld.cn http://www.morning.dschz.cn.gov.cn.dschz.cn http://www.morning.fwwkr.cn.gov.cn.fwwkr.cn http://www.morning.lbqt.cn.gov.cn.lbqt.cn http://www.morning.srjgz.cn.gov.cn.srjgz.cn