socket网络编程——操作篇

一、问题思考

问1.网络通信应用在什么场合?通信的前提是什么?

答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信。通信的前提是如何标识通信进程的唯一,由于不同主机的进程极有可能具有相同的PID,因此,在网络中单单靠PID是无法准确进行标识进程身份的,TCP/IP协议族网络层的IP地址可以唯一的标识连入网络的主机。

问2.socket编程重点是什么?

答2.掌握基于TCP、UDP的S/C架构的编程要点;掌握网络通信方式之间的区别和应用场合。

问3.什么是网络模型?

答3.网络模型主要分为4层,从上至下依次为:应用层、运输层、协议层、链路层。如图所示。

问4.什么是socket套接字?

答4.是一种文件描述符。有三种类型:

①流式套接字(SOCK_STREAM)——过程类似于打电话。提供可靠的、面向连接的通信流,对应使用TCP/IP协议,能够保证数据传输的正确性和顺序性。

②数据报套接字(SOCK_DGRAM)——过程类似于手机之间发短信。无连接服务、数据的传输是独立的不需要经过对方的响应,可靠性无保证。对应使用UDP/IP协议。

③原始套接字(SOCK_RAW)——该套接字直接基于IP地址。

二、TCP、UDP通信编程模型

主要相关操作函数

1. 创建套接字

#include<sys/types.h>
#include<sys/socket.h>
int socket(int protofamil,int type,int protocol);

protofamil 设置协议族即设置socket的地址类型,定义在/usr/include/bits/socket.h 内,有:AF_INET(IPV4——32位IP、16位的端口号)、AF_INET6(IPV6)、AF_LOCAL(Unix域socket)、AF_ROUTE等等;

type 常用的有SOCK_STREAM、SOCK_DGRAM和SOCK_RAW;

protocol 用来指定socket所使用的传输协议编号,通常此参考不用管它,设为0即可。

参数含义

返回值:成功——socket描述符;失败——(-1)

2.绑定地址

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,struct sockaddr * my_addr,int addrlen);

sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
my_addr:指向sockfd要绑定的协议地址。IPv4对应的协议地址结构为:
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};
/* Internet address. */
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};
addrlen:对应的是地址的长度,使用sizeof来计算。

参数含义

返回值:成功——(0),失败——(-1)

3.监听

#include<sys/socket.h>
int listen(int sockfd,int backlog);

参数backlog是能处理的最大连接数目,如果连接数目达此上限则client端将出错。

返回值:成功——(0),失败——(-1)

4.建立连接

#include<sys/types.h>
#include<sys/socket.h>
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);

参数:sockaddr 服务器的socket地址,addrlen为socket地址的长度。

返回值:成功——(0),失败——(-1)

5.接受请求

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr * addr,int * addrlen);

参数:sockfd ——用来监听客户的一个端口号;addr——客户机的地址数据;addrlen即前面这个地址数据的大小

返回值:成功——已建立连接的新的套接字描述符,可用于read/write函数;失败——(-1)

6.读写操作

第①组:用于面向连接的TCP编程中

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

返回值:成功——字节数;失败——(-1)

第②组:用于面向连接的TCP编程中

#include<sys/types.h>
#include<sys/socket.h>
int recv(int sockfd,void *buf,int len,unsigned int flags);
int send(int sockfd,const void * msg,int len,unsigned int falgs);

参数:buf/msg分别指向接收缓冲区和发送的信息,len是接收或发送的最大长度,flags通常设置为0。

返回值:成功——字节数;失败——(-1)

第③组:可用于面向连接和无连接的套接字

#include<sys/types.h>
#include<sys/socket.h>

int recvfrom(int sockfd,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
int sendto ( int sockfd, const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ;

int recvmsg(int sockfd,struct msghdr *msg,unsigned int flags);
int sendmsg(int sockfd,const strcut msghdr *msg,unsigned int flags);

返回值:成功——字节数;失败——(-1)

7.关闭操作

#include<unistd.h>
int close(int fd);

8.其他:申请共享内存

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
函数功能:得到一个共享内存标识符
参数含义:
key:常设置为IPC_PRIVATE(0),建立新共享内存对象,大于0的32位整数:视参数shmflg来确定操作。
size:新建的共享内存大小,以字节为单位
shmflg:根据如下进行设置
0:取共享内存标识符,若不存在则函数会报错;
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符;
IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错;
共享内存的存取权限:S_IRUSR|S_IWUSR...

shmget

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数功能:映射共享内存区到调用该函数的进程地址空间
参数含义:
shmid:共享内存标识符,由shmget函数产生
shmaddr:通常指定为NULL(0)让内核自己决定一个合适的地址位置
shmflg:如果设置为SHM_RDONLY-只读模式,其他值为读写模式
成功:返回共享内存地址

shmat

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
函数功能:断开共享内存连接
参数分析:
shmaddr:要断开共享内存的起始地址

shmdt

三、socket编程框架与示例

1.TCP网络编程设计:TCP并发服务器设计——每一个客户机的请求由服务器创建的一个子进程/线程来处理,不是用服务器进程来处理(TCP循环服务器:一次只能处理一个客户机额请求)。优点:可以“同时”响应多个客户端的服务请求。

代码功能实现:客户端A和B通过服务端进行通信

  1 //编译指令:gcc -o server server.c
  2 //执行指令:./server
  3 #include<stdio.h>
  4 #include<stdlib.h>
  5 #include<sys/types.h>  //数据类型定义
  6 #include<sys/stat.h>
  7 #include<netinet/in.h>  //定义数据结构sockaddr_in
  8 #include<sys/socket.h>  //提供socket函数及数据结构
  9 #include<string.h>
 10 #include<unistd.h>
 11 #include<signal.h>
 12 #include<sys/ipc.h>
 13 #include<errno.h>
 14 #include<sys/shm.h>
 15 #include<time.h>
 16 #define PERM S_IRUSR|S_IWUSR
 17 #define MYPORT      5000     //宏定义定义通信端口
 18 #define MAX_CLIENT  5        //宏定义,定义服务程序可以连接的最大客户数量
 19 #define WELCOME "chating room:"  //宏定义,当客户端连接服务端时,想客户发送此欢迎字符串
 20
 21 //转换函数,将int类型转换成char *类型
 22 static void itoa(int i,char*string)
 23 {
 24     int power,j;
 25     j=i;
 26     for(power=1;j>=10;j/=10)
 27     power*=10;
 28     for(;power>0;power/=10)
 29     {
 30         *string++=‘0‘+i/power;
 31          i%=power;
 32     }
 33     *string=‘\0‘;
 34 }
 35
 36 //得到当前系统时间,并将时间转换成字符串形式存放time_str指向处。
 37 void get_cur_time(char * time_str)
 38 {
 39     time_t timep;
 40     struct tm *p_curtime;
 41     char *time_tmp;
 42     time_tmp=(char *)malloc(2);
 43     memset(time_tmp,0,2);
 44     memset(time_str,0,20);
 45     time(&timep);
 46     p_curtime = localtime(&timep);
 47     strcat(time_str," (");
 48     itoa(p_curtime->tm_hour,time_tmp);
 49     strcat(time_str,time_tmp);
 50     strcat(time_str,":");
 51     itoa(p_curtime->tm_min,time_tmp);
 52     strcat(time_str,time_tmp);
 53     strcat(time_str,":");
 54     itoa(p_curtime->tm_sec,time_tmp);
 55     strcat(time_str,time_tmp);
 56     strcat(time_str,")");
 57     free(time_tmp);
 58 }
 59
 60 //创建共享存储区
 61 key_t shm_create()
 62 {
 63     key_t shmid;  //key_t实际上是int型
 64     if((shmid = shmget(IPC_PRIVATE,1024,PERM)) == -1)
 65     {
 66         fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));
 67         exit(1);
 68     }
 69     return shmid; //返回共享内存的标识符
 70 }
 71
 72 //端口绑定函数:创建套接字,并绑定到指定端口
 73 int bindPort(unsigned short int port)
 74 {
 75     int sockfd;
 76     struct sockaddr_in my_addr;
 77     sockfd = socket(AF_INET,SOCK_STREAM,0);//创建基于流套接字
 78     my_addr.sin_family = AF_INET;//IPv4协议族
 79     my_addr.sin_port = htons(port);//端口转换
 80     my_addr.sin_addr.s_addr = INADDR_ANY;
 81     bzero(&(my_addr.sin_zero),0);
 82     if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)) == -1)
 83     {
 84         perror("bind");
 85         exit(1);
 86     }
 87     printf("bing success!\n");
 88     return sockfd;
 89 }
 90
 91 int main(int argc, char *argv[])
 92 {
 93     int sockfd,clientfd,sin_size,recvbytes; //定义监听套接字、客户套接字
 94     pid_t pid,ppid;
 95     char *buf, *r_addr, *w_addr, *temp, *time_str;//="\0"; //定义临时存储区
 96     struct sockaddr_in their_addr;  //定义地址结构
 97     key_t shmid;
 98     shmid = shm_create(); //创建共享存储区
 99     temp = (char *)malloc(255);
100     time_str=(char *)malloc(20);
101
102     sockfd = bindPort(MYPORT);//绑定端口
103
104     while(1)
105     {
106         if(listen(sockfd,MAX_CLIENT) == -1)//在指定端口上监听
107         {
108             perror("listen");
109             exit(1);
110         }
111         printf("listening......\n");
112         if((clientfd = accept(sockfd,(struct sockaddr*)&their_addr,&sin_size)) == -1)//接收客户端连接
113         {
114             perror("accept");
115             exit(1);
116         }
117         printf("accept from:%d\n",inet_ntoa(their_addr.sin_addr));
118         send(clientfd,WELCOME,strlen(WELCOME),0);//发送问候信息
119         buf = (char *)malloc(255);
120         ppid = fork();     //创建子进程
121         if(ppid == 0)
122         {
123             pid = fork();  //创建子进程 -- TCP并发服务器
124             while(1)
125             {
126                 if(pid > 0)
127                 {
128                     //父进程:接收信息
129                     memset(buf,0,255);
130                     if((recvbytes = recv(clientfd,buf,255,0)) <= 0)
131                     {
132                         perror("recv1");
133                         close(clientfd);
134                         raise(SIGKILL);
135                         exit(1);
136                     }
137                     //write buf‘s data to share memory
138                     w_addr = shmat(shmid, 0, 0);
139                     memset(w_addr, ‘\0‘, 1024);
140                     strncpy(w_addr, buf, 1024);
141                     get_cur_time(time_str);
142                     strcat(buf,time_str);
143                     printf(" %s\n",buf);
144                 }
145                 else if(pid == 0)
146                 {
147                     //子进程用于发送信息
148                     sleep(1);
149                     r_addr = shmat(shmid, 0, 0);
150                     if(strcmp(temp,r_addr) != 0)
151                     {
152                         strcpy(temp,r_addr);
153                         get_cur_time(time_str);
154                         strcat(r_addr,time_str);
155                         if(send(clientfd,r_addr,strlen(r_addr),0) == -1)
156                         {
157                             perror("send");
158                         }
159                         memset(r_addr, ‘\0‘, 1024);
160                         strcpy(r_addr,temp);
161                     }
162                 }
163                 else
164                     perror("fork");
165             }
166         }
167     }
168     printf("------------------------------\n");
169     free(buf);
170     close(sockfd);
171     close(clientfd);
172     return 0;
173 }

server.c

 1 //编译指令:gcc -o client client.c
 2 //执行指令:./client 192.168.1.107 5000 user_name
 3 #include<stdio.h>
 4 #include<netinet/in.h>  //定义数据结构sockaddr_in
 5 #include<sys/socket.h>  //提供socket函数及数据结构
 6 #include<sys/types.h>   //数据类型定义
 7 #include<string.h>
 8 #include<stdlib.h>
 9 #include<netdb.h>
10 #include<unistd.h>
11 #include<signal.h>
12 #include<time.h>
13
14 int main(int argc, char *argv[])
15 {
16     struct sockaddr_in clientaddr;//定义地址结构
17     pid_t pid;
18     int clientfd,sendbytes,recvbytes;//定义客户端套接字
19     struct hostent *host;
20     char *buf,*buf_r;
21     if(argc < 4)
22     {
23         printf("usage:\n");
24         printf("%s host port name\n",argv[0]);
25         exit(1);
26     }
27     host = gethostbyname(argv[1]);
28     if((clientfd = socket(AF_INET,SOCK_STREAM,0)) == -1)  //创建客户端套接字
29     {
30         perror("socket\n");
31         exit(1);
32     }
33     //绑定客户端套接字
34     clientaddr.sin_family = AF_INET;
35     clientaddr.sin_port = htons((uint16_t)atoi(argv[2]));
36     clientaddr.sin_addr = *((struct in_addr *)host->h_addr);
37     bzero(&(clientaddr.sin_zero),0);
38     if(connect(clientfd,(struct sockaddr *)&clientaddr,sizeof(struct sockaddr)) == -1) //连接服务端
39     {
40         perror("connect error.\n");
41         exit(1);
42     }
43     buf=(char *)malloc(120);
44     memset(buf,0,120);
45     buf_r=(char *)malloc(100);
46     if( recv(clientfd,buf,100,0) == -1)
47     {
48         perror("recv:");
49         exit(1);
50     }
51     printf("\n%s\n",buf);
52     pid = fork();//创建子进程
53     while(1)
54     {
55         if(pid > 0)
56         {
57             //父进程:发送信息
58             strcpy(buf,argv[3]);
59             strcat(buf,":");
60             memset(buf_r,0,100);
61             fgets(buf_r,100,stdin); //从标准输入获取
62             strncat(buf,buf_r,strlen(buf_r)-1);  //连接两个字符串
63             if((sendbytes = send(clientfd,buf,strlen(buf),0)) == -1)
64             {
65                 perror("send\n");
66                 exit(1);
67             }
68         }
69         else if(pid == 0)
70         {
71             //子进程:接收信息
72             memset(buf,0,100);
73             if(recv(clientfd,buf,100,0) <= 0)
74             {
75                 perror("recv:");
76                 close(clientfd);
77                 raise(SIGSTOP);
78                 exit(1);
79             }
80             printf("%s\n",buf);
81         }
82         else
83             perror("fork error.");
84     }
85     close(clientfd);
86     return 0;
87 }

client.c

提示:运行测试程序时,客户端A/B和服务端分别在一个终端页面进行。

2.UDP网络编程设计:UDP循环服务器设计——server每一次从socket上获得client的请求,然后进行处理,再把结果返回给client,如此循环。缺点:如果一个客户端一直占有这个服务端,其他的客户端就不能获得服务端的服务。

 1 //编译指令:gcc -o server server.c
 2 //运行指令:./server
 3 #include <stdlib.h>
 4 #include <stdio.h>
 5 #include <errno.h>
 6 #include <string.h>
 7 #include <unistd.h>
 8 #include <netdb.h>
 9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <sys/types.h>
12 #include <arpa/inet.h>
13
14 #define SERVER_PORT  5001
15 #define MAX_MSG_SIZE 1024
16
17 int main(void)
18 {
19     int sockfd;
20     struct sockaddr_in addr,addr1;
21     int addrlen,n; 

22     char msg[MAX_MSG_SIZE];
23
24     /* 服务器端建立socket描述符 */
25     sockfd=socket(AF_INET,SOCK_DGRAM,0);
26     if(sockfd<0)
27     {
28         fprintf(stderr,"Socket Error:%s\n",strerror(errno));
29         exit(1);
30     }
31
32     bzero(&addr,sizeof(struct sockaddr_in));
33     addr.sin_family=AF_INET;
34     addr.sin_addr.s_addr=htonl(INADDR_ANY);
35     addr.sin_port=htons(SERVER_PORT);
36
37     if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0)
38     {
39         fprintf(stderr,"Bind Error:%s\n",strerror(errno));
40         exit(1);
41     }
42
43     while(1)
44     {
45         bzero(msg,sizeof(msg));
46         addrlen = sizeof(struct sockaddr);
47         n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&addr1,&addrlen); // 从客户端接收消息
48         msg[n]=0;//添加上字符串结束标志
49         fprintf(stdout,"Server have received %s",msg); // 显示消息
50     }
51
52     close(sockfd);
53 } 

server.c

 1 //编译指令:gcc -o client client.c
 2 //运行指令:./client 192.168.1.107
 3 #include <stdlib.h>
 4 #include <stdio.h>
 5 #include <errno.h>
 6 #include <string.h>
 7 #include <unistd.h>
 8 #include <netdb.h>
 9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <sys/types.h>
12 #include <arpa/inet.h>
13
14 #define SERVER_PORT  5001
15 #define MAX_BUF_SIZE 1024
16
17 int main(int argc,char *argv[])
18 {
19     int sockfd;
20     struct sockaddr_in addr;
21     char buffer[MAX_BUF_SIZE];
22     int n;
23
24     if(argc!=2)
25     {
26         fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
27         exit(1);
28     }
29
30     /* 建立 sockfd描述符 */
31     sockfd=socket(AF_INET,SOCK_DGRAM,0);
32     if(sockfd<0)
33     {
34         fprintf(stderr,"Socket Error:%s\n",strerror(errno));
35         exit(1);
36     }
37
38     bzero(&addr,sizeof(struct sockaddr_in));
39     addr.sin_family=AF_INET;
40     addr.sin_port=htons(SERVER_PORT);
41     if(inet_aton(argv[1],&addr.sin_addr)<0)  //inet_aton函数用于把字符串型的IP地址转化成网络二进制数字
42     {
43         fprintf(stderr,"Ip error:%s\n",strerror(errno));
44         exit(1);
45     }
46
47     while(1)
48     {     /* 从键盘读入,写到服务端 */
49         printf("Please input char:\n");
50         fgets(buffer,MAX_BUF_SIZE,stdin);
51         sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr *)(&addr),sizeof(struct sockaddr_in));
52         bzero(buffer,MAX_BUF_SIZE);
53     }
54
55     close(sockfd);
56 } 

client.c

时间: 2024-10-09 17:07:37

socket网络编程——操作篇的相关文章

C# Socket网络编程精华篇

几个和Socket编程紧密相关的概念: TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用.http协议在应用层运行. 02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP 提供传输保证. 03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP.IGMP.RIP.OSPF.IP(v4,v6)等

C# Socket网络编程精华篇(转)

我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用.http协议在应用层运行. 02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP 提供传输保证. 03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP.IGMP.RI

python_day8 socket 网络编程 基本篇

服务端: server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)server.bind(('127.0.0.1',80))server.listen(5)conn,address=server.accept()data=conn.recv(1024)print(data)conn.send(data.upper())conn.close()server.close() 客户端 import socketclient=socket.socke

Socket网络编程(TCP/IP/端口/类)和实例

Socket网络编程(TCP/IP/端口/类)和实例 原文:C# Socket网络编程精华篇 转自:微冷的雨 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用.http协议在应用层运行. 02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而

Java基础篇Socket网络编程中的应用实例

说到java网络通讯章节的内容,刚入门的学员可能会感到比较头疼,应为Socket通信中一定会伴随有IO流的操作,当然对IO流比较熟练的哥们会觉得这是比较好玩的一章,因为一切都在他们的掌握之中,这样操作起来就显得非常得心应手,但是对于IO本来就不是多熟悉的哥们来说就有一定的困难了,在搞清楚IO流操作机制的同时还必须会应用到Socket通信中去,否则会对得到的结果感到非常郁闷和懊恼,下面就和大家一起分享一下自己遇到一点小麻烦后的感触以及给出的解决办法. 要求:客户端通过Socket通信技术上传本地一

Python之路【第九篇】:Python基础(25)——socket网络编程

socket网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用打开.读写.关闭模式来操作.socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO.打开.关闭) socket和file的区别: file模块是针对某个指定

Socket网络编程--小小网盘程序(1)

这个系列是准备讲基于Linux Socket进行文件传输.简单的文件传输就是客户端可以上传文件,可以从服务器端下载文件.就这么两个功能如果再加上身份验证,就成了FTP服务器了,如果对用户的操作再加上一些功能(如分享),就可以作为一个最简单的网盘了.想想是不是有点小激动啊. 我这一小节就不讲那么高级的东西,就先了解文件怎么传输,我们以前的聊天程序传输数据都是一次发送就完成本次的发送,因为一个sendBuf是足够的.但是对于二进制文件来说,文件的大小就不一定了,有可能很大,所以我们的Buf是不知道要

socket网络编程的一些基础知识

源地址:http://blog.csdn.net/roger_77/article/details/1453049 目录: 1) 什么是套接字? 2) Internet 套接字的两种类型 3) 网络理论 4) 结构体 5) 本机转换 6) IP 地址和如何处理它们 7) socket()函数 8) bind()函数 9) connect()函数 10) listen()函数 11) accept()函数 12) send()和recv()函数 13) sendto()和recvfrom()函数 

万物互联之~网络编程深入篇

深入篇¶ 上节回顾:5种IO模型 | IO多路复用 and 万物互联之-网络编程加强篇 官方文档:https://docs.python.org/3/library/internet.html 1.概念回顾¶ 1.1.TCP三次握手¶ 画一张图来通俗化讲讲TCP三次握手: 用代码来说,大概过程就是: 1.2.TCP四次挥手¶ 画图通俗讲下TCP四次挥手: 用代码来说,大概过程就是: 其实这个也很好的解释了之前的端口占用问题,如果是服务端先断开连接,那么服务器就是四次挥手的发送方,最后一次消息是得