编写服务端程序server.c
#include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <netinet/in.h> #define MAXLINE 1024 int main() { int listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd == -1) { printf("socket error[%d]:%s\n", errno, strerror(errno)); exit(errno); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(55555); if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { printf("bind error[%d]:%s\n", errno, strerror(errno)); exit(errno); } if (listen(listen_fd, 3) == -1) { printf("listen error[%d]:%s\n", errno, strerror(errno)); exit(errno); } while (1) { struct sockaddr_in client_addr; memset(&client_addr, 0, sizeof(client_addr)); int len = sizeof(client_addr); int client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &len); if (client_fd == -1) { printf("accept error[%d]:%s\n", errno, strerror(errno)); continue; } char rb[100] = {0}; int recv_len = recv(client_fd, rb, MAXLINE, 0); if (recv_len == -1) { printf("recv error[%d]:%s\n", errno, strerror(errno)); continue; } printf("recv[%d]:%s\n", recv_len, rb); strcat(rb, ", has been received by server, the msg from server"); if (send(client_fd, rb, strlen(rb), 0) == -1) { printf("send error[%d]:%s\n", errno, strerror(errno)); continue; } printf("send success\n"); close(client_fd); } close(listen_fd); }
代码说明:
函数原型: int socket(int domain, int type, int protocol)中的参数protocol取值的取值见 netinet/in.h (结合参考/etc/protocols文件)
编写客户端程序client.c
#include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <netinet/in.h> #include <string.h> #define MAXLINE 1024 int main() { int client_fd = socket(AF_INET, SOCK_STREAM, 0); if (client_fd == -1) { printf("socket error[%d]:%s\n", errno, strerror(errno)); exit(errno); } struct sockaddr_in target_addr; memset(&target_addr, 0, sizeof(target_addr)); target_addr.sin_family = AF_INET; target_addr.sin_port = htons(55555); if (inet_pton(AF_INET, "127.0.0.1", &target_addr.sin_addr) < 0) { printf("inet_ptonl error[%d]:%s\n", errno, strerror(errno)); exit(errno); } if (connect(client_fd, (struct sockaddr *)&target_addr, sizeof(target_addr)) < 0) { printf("connect error[%d]:%s\n", errno, strerror(errno)); exit(errno); } char sb[500]={0}; strcpy(sb, "client message"); if (send(client_fd, sb, strlen(sb), 0) < 0) { printf("send error[%d]:%s\n", errno, strerror(errno)); exit(errno); } printf("send success\n"); char rb[MAXLINE]={0}; if (recv(client_fd, rb, MAXLINE, 0) < 0) { printf("recv error[%d]:%s\n", errno, strerror(errno)); exit(errno); } printf("recv from server:%s\n", rb); }
编译
$gcc client.c -o client
运行
1. 启动server
recv[14]:client message
send success
2. tcpdump观察
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
09:41:12.535453 IP localhost.30256 > localhost.55555: Flags [S], seq 4085943811, win 32792, options [mss 16396,sackOK,TS val 2860232704 ecr 0,nop,wscale 7], length 0
09:41:12.535526 IP localhost.55555 > localhost.30256: Flags [S.], seq 555658646, ack 4085943812, win 32768, options [mss 16396,sackOK,TS val 2860232705 ecr 2860232704,nop,wscale 7], length 0
09:41:12.535547 IP localhost.30256 > localhost.55555: Flags [.], ack 1, win 257, options [nop,nop,TS val 2860232705 ecr 2860232705], length 0
09:41:12.535614 IP localhost.30256 > localhost.55555: Flags [P.], seq 1:15, ack 1, win 257, options [nop,nop,TS val 2860232705 ecr 2860232705], length 14
09:41:12.535631 IP localhost.55555 > localhost.30256: Flags [.], ack 15, win 256, options [nop,nop,TS val 2860232705 ecr 2860232705], length 0
09:41:12.535718 IP localhost.55555 > localhost.30256: Flags [P.], seq 1:65, ack 15, win 256, options [nop,nop,TS val 2860232705 ecr 2860232705], length 64
09:41:12.535728 IP localhost.30256 > localhost.55555: Flags [.], ack 65, win 257, options [nop,nop,TS val 2860232705 ecr 2860232705], length 0
09:41:12.535753 IP localhost.55555 > localhost.30256: Flags [F.], seq 65, ack 15, win 256, options [nop,nop,TS val 2860232705 ecr 2860232705], length 0
09:41:12.535902 IP localhost.30256 > localhost.55555: Flags [F.], seq 15, ack 66, win 257, options [nop,nop,TS val 2860232705 ecr 2860232705], length 0
09:41:12.535921 IP localhost.55555 > localhost.30256: Flags [.], ack 16, win 256, options [nop,nop,TS val 2860232705 ecr 2860232705], length 0
3. 启动client
send success
recv from server:client message, has been received by server, the msg from server
以上三步,把结果一起贴出来了,需要启动三个终端,并且按顺序运行以上三个命令
观察监听状态
tcp 0 0 0.0.0.0:55555 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:55555 127.0.0.1:30256 TIME_WAIT
分析
1. server执行完lisetn函数的时候, 开始监听
2. server 执行到accept的时候,阻塞了,等待client的连接
3. client执行connect的时候,client发起三次握手
tcpdump捕捉到三次握手的信息
10:03:51.122096 IP localhost.55555 > localhost.30259: Flags [S.], seq 3786610237, ack 261830420, win 32768, options [mss 16396,sackOK,TS val 2861591291 ecr 2861591291,nop,wscale 7], length 0
10:03:51.122116 IP localhost.30259 > localhost.55555: Flags [.], ack 1, win 257, options [nop,nop,TS val 2861591291 ecr 2861591291], length 0
tcp 0 0 127.0.0.1:30259 127.0.0.1:55555 ESTABLISHED
tcp 0 0 127.0.0.1:55555 127.0.0.1:30259 ESTABLISHED
server accept的阻塞解除,执行到下一行
4. client继续执行,执行完send函数的时候, 发生了client到server的数据交互
10:19:21.731810 IP localhost.55555 > localhost.30259: Flags [.], ack 15, win 256, options [nop,nop,TS val 2862521901 ecr 2862521901], length 0
5. server继续执行,执行recv的时候,从内核缓存中取出client发过来的数据(tcpdump并没有变化可以看出是在缓存中接收)
6. server继续执行,执行完send函数发消息给client的时候,tcpdump捕捉到server到client的数据交互
10:23:41.110764 IP localhost.30259 > localhost.55555: Flags [.], ack 65, win 257, options [nop,nop,TS val 2862781280 ecr 2862781280], length 0
7. server继续执行,执行到close(client_fd)的时候,tcpdump捕捉到断开的握手信息
10:24:36.775306 IP localhost.30259 > localhost.55555: Flags [.], ack 66, win 257, options [nop,nop,TS val 2862836945 ecr 2862836905], length 0
网络状态变为
tcp 65 0 127.0.0.1:30259 127.0.0.1:55555 CLOSE_WAIT
tcp 0 0 127.0.0.1:55555 127.0.0.1:30259 FIN_WAIT2
8. 此时, 关闭了server -> client方向的数据流
9. client继续执行,执行完recv函数,获取到server发送过来的数据(依然从缓存中得到)
10. client运行完close(client_fd)函数, 发起断开握手
10:25:34.171108 IP localhost.55555 > localhost.30259: Flags [.], ack 16, win 256, options [nop,nop,TS val 2862894340 ecr 2862894340], length 0
11. 网络状态变成
tcp 0 0 127.0.0.1:55555 127.0.0.1:30259 TIME_WAIT

状态变迁
建立连接时的状态变迁
一开始,建立连接之前服务器和客户端的状态都为CLOSED。服务器创建socket后开始监听,变为LISTEN状态。客户端请求建立连接,向服务器发送SYN报文,客户端的状态变为SYN_SENT。服务器收到客户端的报文后向客户端发送ACK和SYN报文,此时服务器的状态变为SYN_RCVD。然后,客户端收到ACK、SYN,就向服务器发送ACK,客户端状态变为ESTABLISHED,服务器收到客户端的ACK后也变为ESTABLISHED。此时,3次握手完成,连接建立!
断开连接时的状态变迁
由于tcp连接是全双工的,断开连接会比建立连接麻烦一点点。客户端先向服务器发送FIN报文,请求断开连接,其状态变为FIN_WAIT1。服务器收到FIN后向客户端发生ACK,服务器状态变为CLOSE_WAIT。客户端收到ACK后就进入FIN_WAIT2状态。此时连接已经断开了一半了。如果服务器还有数据要发送给客户端,就会继续发送。直到发完了,就发送FIN报文,此时服务器进入LAST_ACK状态。客户端收到服务器的FIN后,马上发送ACK给服务器,此时客户端进入TIME_WAIT状态,再过了2MSL长的时间后进入CLOSED状态。服务器收到客户端的ACK就进入CLOSED状态。
至此,还有一个状态没有提及:CLOSING状态。CLOSING状态表示客户端发生了FIN,但没有收到服务器的ACK,却收到了服务器的FIN。这种情况发生在服务器发送的ACK丢包的时候,因为网络传输有时会有意外。
相关推荐
### Linux Socket基础知识详解 #### 一、网络进程通信机制 在网络环境中,进程通信是指不同主机上的进程能够相互发送和接收信息。为了使这种通信成为可能,必须解决几个关键问题: 1. **进程标识**:在单机环境中...
### Linux Socket基础教程知识点解析 #### 一、什么是Socket? Socket是一种用于进程间通信的机制,允许不同计算机上的进程之间进行通信。在Unix系统中,一切皆为文件,包括网络连接在内的各种通信方式都可以被视...
一、Linux Socket基础 Socket是操作系统提供的一种接口,用于在网络环境中实现进程间通信。在Linux系统中,它基于Berkeley套接字(BSD Sockets)模型,为应用程序提供了创建、配置和管理网络连接的API。 二、Socket...
这本书通过实例展示了如何在Linux环境下进行网络通信,涵盖了从基础的socket创建、连接到高级的多线程、多进程并发处理等核心知识点。源代码的提供为读者提供了实践和学习的宝贵材料。 首先,我们要理解什么是...
在IT行业中,Linux Socket是进行网络通信的重要工具,尤其对于系统和网络程序员来说,理解和掌握Linux Socket编程至关重要。本示例代码提供了客户端(client)和服务器端(server)的实现,帮助初学者深入理解如何在...
本教程覆盖了从基础概念到高级特性的广泛内容,帮助开发者理解和应用Linux Socket进行网络编程。 首先,让我们从基础开始。Linux Socket学习(二).txt可能涵盖了Socket的基本概念,包括Socket的定义、Socket API的...
在IT领域,Linux Socket编程是网络通信的核心技术之一,它为进程间通信提供了接口,尤其在服务器开发中扮演着重要角色。本实例将探讨如何在Linux环境下使用Socket函数调用来实现一个简单的Server向Client发送消息的...
总结,Linux Socket是网络编程的基础,理解和掌握其工作原理对于开发高效、可靠的网络应用至关重要。通过对socket发送数据函数流程、sys_socket流程和接收数据过程的深入理解,开发者可以更好地控制网络通信的每一个...
1. **Linux Socket基础** - **Socket概述**:Socket是网络编程的一个抽象概念,它提供了一种在网络中进行进程间通信(IPC)的方法。在Linux中,Socket分为流式(SOCK_STREAM)和数据报(SOCK_DGRAM)两种类型,前者...
在IT领域,Linux Socket编程是网络通信的核心技术之一,它为开发者提供了在Linux操作系统上实现进程间通信(IPC)和网络通信的接口。本实战指南将深入探讨这一主题,帮助你掌握如何在Linux环境中构建高效的网络应用...
Socket是网络通信的基础,它是两台机器之间建立连接的接口。在Linux中,它是一个文件描述符,可以使用标准的I/O函数进行读写操作。 2. **socket函数创建**: 使用`socket()`函数创建一个socket,需要指定协议族...
Linux Socket编程基础是计算机网络通信领域中的重要一环,它主要涉及如何在Linux操作系统中创建、管理和使用套接字(socket)进行进程间通信或网络数据传输。在本篇文章中,我们将深入探讨Linux Socket编程的基本...
本压缩包“linuxsocket.zip”包含了基于TCP/IP协议的socket通信测试代码,是学习Linux应用编程的一个实用资源。这里我们将深入探讨Linux TCP/IP socket编程的相关知识点。 1. **TCP/IP协议栈**:TCP/IP协议栈是...
1. **Socket基础知识**:Socket是网络通信的基础,它是进程间通信的一种方式,特别适用于跨网络的通信。在Linux系统中,Socket接口提供了丰富的API供开发者使用,如socket()函数创建Socket,bind()绑定本地地址,...
1. **Socket基础概念**:Socket是进程间通信的一种方式,特别用于网络通信。它在应用程序和网络协议栈之间建立连接,允许数据在网络中传输。在Linux系统中,Socket基于BSD Socket API。 2. **Socket类型**:在Linux...
Linux下使用C++进行Socket编程是一门涉及网络通信的高级技术。在Linux操作系统中,Socket编程通常采用C语言,因为传统的GNU C库提供了丰富的Socket API函数,但这些函数是面向过程设计的,没有面向对象的封装,使用...
1. **Socket基础概念** - **Socket接口**:Socket是应用程序与网络协议栈之间的接口,提供了一种标准的方法来创建、连接和交换数据。 - **TCP/IP协议族**:Linux Socket主要基于TCP/IP协议族,包括TCP(传输控制...
1. **Socket基础知识**:包括Socket的创建、绑定、监听、接受连接和发送/接收数据等基本操作。 2. **TCP和UDP的区别**:TCP提供面向连接的服务,确保数据的可靠传输,而UDP则为无连接服务,速度快但不保证数据的顺序...
### Linux Socket Programming (Linux 套接字编程) #### 知识点概览: 1. **Socket编程基础** - **Socket概念介绍** - **Socket的用途与应用场景** 2. **基本Socket概念** - **Socket域和地址族** - **Socket...
在IT行业中,网络通信是至关重要的部分,而Linux Socket编程是实现这一目标的关键技术。Socket是一种接口,允许应用程序通过网络发送和接收数据。本篇将深入探讨标题为"Linux Socket两则示例"的资源,其中包括`echo_...