图说使用socket建立TCP连接

在网络应用如火如荼的今天,熟悉TCP/IP网络编程,那是最好不过。如果你并不非常熟悉,不妨花几分钟读一读。 为了帮助快速理解,先上个图,典型的使用socket建立和使用TCP/UDP连接过程为(截图来源戳这里):

下面仅讲述TCP连接建立的过程。 (参考资料来自这里)

1. Initial State (初始阶段)

o TCP is connection based, ... establishing it is a complex multistage process
o initially all machines are the same
o no special ‘server‘ machines
o the difference is all in the software
  • TCP是面向连接的协议,TCP连接的建立是一个复杂的多阶段的过程
  • 最开始所有机器状态都是一样的
  • 并不存在所谓的‘server‘机器
  • 所有的区别仅存在于软件之中

2. Passive Open (被动Open)

o server process does a ‘passive‘ open on a port
o it waits for a client to connect
o at this stage there is no Internet network traffic
o tells the TCP layer which process to connect to
  • 服务器端进程在某个端口上执行一个"被动"open
  • 服务器端进程等待某个客户端来连接
  • 这一阶段并不存在Internet网络传输
  • 告知TCP层哪一个进程可以接受连接

3. Active Open (主动Open)

o client process usually on a different machine
o performs an ‘active‘ open on the port
o port number at the client end is needed
          usually automatic (e.g. 2397)
          but can be chosen
o network message -> server machine
          requests connection
  • 客户端进程通常情况下运行在另外一个机器上
  • 客户端进程在某个端口上执行一个"主动"Open
  • 在客户端,端口号也是需要的,通常是自动分配的(例如:2397),也可以主动选择一个端口号
  • 网络消息->服务器机器,需要一个连接

4. Rendezvous (集结,也就是连接建立)

o server side accepts and TCP connection established
o a bi-directional reliable byte-stream
o connection identified by both host/port numbers
  e.g. <151.10017.25:2397/161.112.192.5:21>
o server port is not consumed
o can stay ‘passive‘ open for more connections
o like telephone call desk: one number many lines
  • 服务器端接受连接,TCP连接得以建立
  • 连接是一个双向的可靠字节流(即全双工可靠字节流)
  • 识别一个连接,可以用host/port对,例如: 151.10017.25:2397/161.112.192.5:21
  • 服务器端口并没有被消耗殆尽
  • 服务器端可以一直处于"被动"open状态以接收更多的连接请求
  • 类似于电话呼叫台: 一个号码多条线路

5. More

o other clients can connect to the same port
o state for connections in the client/server only
o no information needed in the network
  not like old style relay-based exchanges
o server can restrict access to specified host or port
o server can find out connected host/port
  • 其他客户端可以连接到同一个端口
  • 连接状态仅存在于client/server中
  • 与老式风格的基于中继的交换有所不同,tcp连接网络中不需要信息
  • 服务器可以对指定的主机或端口进行访问限制
  • 在服务器上可以找出已经连接的主机/端口

有关Passive open和Active open,区别如下:

passive - patient but lazy
 active - industrious but impatient

passive : waits for request for connection
        : waits for ever
 active : sends out request for connection
        : times out

o normally server does passive open
  waiting for client
o but not always (e.g. ftp)
o active opens can rendezvous ... but may miss due to time-outs
o either can specify local port
  but if not specified, allocated automatically

到此为止,我们已经弄明白了TCP连接建立的过程。下面给出一个简单的TCP client/server实现。

1. libfoo.h

 1 #ifndef _LIBFOO_H
 2 #define _LIBFOO_H
 3
 4 #ifdef __cplusplus
 5 extern "C" {
 6 #endif
 7
 8 #define PORT 2345
 9
10 extern int send_file(int sockfd, char *dstfile, char *srcfile);
11 extern int recv_file(int sockfd);
12
13 #ifdef __cplusplus
14 }
15 #endif
16
17 #endif /* _LIBFOO_H */

2. libfoo.c

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <errno.h>
  4 #include <string.h>
  5 #include <fcntl.h>
  6 #include <sys/stat.h>
  7 #include <sys/types.h>
  8 #include <arpa/inet.h>
  9 #include "libfoo.h"
 10
 11 typedef struct file_header_s {
 12         char *file[1024]; /* (dst) file absolute path */
 13         size_t size;      /* file size */
 14         mode_t mode;      /* file mode */
 15 } file_header_t;
 16
 17 /*
 18  * send file via sockfd
 19  */
 20 int
 21 send_file(int sockfd, char *dstfile, char *srcfile)
 22 {
 23         int             fd;
 24         file_header_t   fhr;
 25         struct stat     sb;
 26         int             rc;
 27         int             n, m;
 28         size_t          count;
 29         char            buf[1024] = { 0 };
 30
 31         /* open src file */
 32         if ((fd = open(srcfile, O_RDONLY)) < 0) {
 33                 fprintf(stderr, "cannot open `%s‘ for reading: %s\n",
 34                     srcfile, strerror(errno));
 35                 return 1;
 36         }
 37
 38         /* stat src file */
 39         if ((rc = fstat(fd, &sb)) < 0) {
 40                 fprintf(stderr, "cannot stat fd %d: %s\n",
 41                     fd, strerror(errno));
 42                 rc = 2;
 43                 goto done;
 44         }
 45
 46         rc = 0;
 47
 48         /*
 49          * create dst file header and send out
 50          * o dst file path != src file path in case they are on the same host
 51          * o dst file size == src file size
 52          * o dst file mode == src file mode
 53          */
 54         memset(&fhr, 0, sizeof (fhr));
 55         strncpy((char *)fhr.file, dstfile, strlen(dstfile));
 56         fhr.size = htonl(sb.st_size);
 57         fhr.mode = htonl(sb.st_mode & ~S_IFMT);
 58
 59         /* write file header to sockfd */
 60         if ((n = write(sockfd, &fhr, sizeof (fhr))) == -1) {
 61                 fprintf(stderr, "cannot write file header to sock %d: %s\n",
 62                     sockfd, strerror(errno));
 63                 rc = 3;
 64                 goto done;
 65         }
 66
 67         /* read from fd, write to sockfd */
 68         count = sb.st_size;
 69         while (count > 0) {
 70                 m = (count >= sizeof (buf)) ? sizeof(buf) : count;
 71
 72                 memset(buf, 0, sizeof (buf));
 73                 if ((n = read(fd, buf, m)) == -1) {
 74                         fprintf(stderr, "fail to read %d bytes from fd %d\n",
 75                             m, fd);
 76                         rc = 4;
 77                         goto done;
 78                 }
 79
 80                 if ((n = write(sockfd, buf, m)) == -1) {
 81                         fprintf(stderr, "fail to write %d bytes to sock %d\n",
 82                             m, sockfd);
 83                         rc = 5;
 84                         goto done;
 85                 }
 86
 87                 count -= m;
 88         }
 89
 90 done:
 91         close(fd);
 92         return rc;
 93 }
 94
 95 /*
 96  * read from sockfd then save to file
 97  */
 98 int
 99 recv_file(int sockfd)
100 {
101         int             fd;
102         file_header_t   fhr;
103         char            *file = NULL;
104         size_t          filesize;
105         mode_t          filemode;
106         size_t          count;
107         int             rc;
108         int             n, m;
109         char            buf[1024] = { 0 };
110
111         /* 1. read file header */
112         if ((n = read(sockfd, &fhr, sizeof (fhr))) == -1) {
113                 fprintf(stderr, "fail to read file header from sock %d: %s\n",
114                     sockfd, strerror(errno));
115                 return 1;
116         }
117         file = (char *)fhr.file;
118         filesize = ntohl(fhr.size);
119         filemode = ntohl(fhr.mode);
120         printf("> dst filename=%s, filesize=%u, filemode=%o\n",
121             file, filesize, (int)filemode);
122
123         /* 2. open file to save */
124         (void) umask(0);
125         if ((fd = open(file, O_RDWR|O_CREAT|O_TRUNC, filemode)) < 0) {
126                 fprintf(stderr, "cannot create `%s‘ for writing: %s\n",
127                     file, strerror(errno));
128                 return 2;
129         }
130
131         rc = 0;
132
133         /* 3. read from sockfd and write to fd */
134         count = filesize;
135         while (count > 0) {
136                 m = count >= sizeof (buf) ? sizeof(buf) : count;
137
138                 memset(buf, 0, sizeof (buf));
139                 if ((n = read(sockfd, buf, m)) == -1) {
140                         fprintf(stderr, "fail to read %d bytes from sock %d\n",
141                             m, sockfd);
142                         rc = 4;
143                         goto done;
144                 }
145
146                 if ((n = write(fd, buf, m)) == -1) {
147                         fprintf(stderr, "fail to write %d bytes to file %d\n",
148                             m, fd);
149                         rc = 5;
150                         goto done;
151                 }
152
153                 count -= m;
154         }
155
156 done:
157         close(fd);
158         return rc;
159 }

3. server.c

  1 /**
  2  * server.c - single connection tcp server
  3  */
  4
  5 #include <stdio.h>
  6 #include <unistd.h>
  7 #include <errno.h>
  8 #include <string.h>
  9 #include <sys/types.h>
 10 #include <sys/types.h>
 11 #include <sys/stat.h>
 12 #include <fcntl.h>
 13 #include <sys/socket.h>
 14 #include <arpa/inet.h>
 15 #include "libfoo.h"
 16
 17 int
 18 main(int argc, char *argv[])
 19 {
 20         int port = PORT;
 21         int rc = 0;
 22
 23         if (argc != 2) {
 24                 fprintf(stderr, "Usage: %s <ipv4>\n", argv[0]);
 25                 return -1;
 26         }
 27         char *ipv4 = argv[1];
 28
 29         /* 1. socket */
 30         int listen_fd = socket(PF_INET, SOCK_STREAM, 0);
 31         if (listen_fd < 0) {
 32                 fprintf(stderr, "E: socket(),%d,%s\n",
 33                     errno, strerror(errno));
 34                 return -1;
 35         }
 36
 37         /* 2. bind */
 38         struct sockaddr_in srv_addr;
 39         memset(&srv_addr, 0, sizeof (struct sockaddr_in));
 40         srv_addr.sin_family = AF_INET;
 41
 42         rc = inet_pton(AF_INET, ipv4, &srv_addr.sin_addr);
 43         if (rc != 1) {
 44                 fprintf(stderr, "E: inet_pton(),%d,%s\n",
 45                     errno, strerror(errno));
 46                 return -1;
 47         }
 48         srv_addr.sin_port = htons(port);
 49
 50         rc = bind(listen_fd, (struct sockaddr *)&srv_addr, sizeof (srv_addr));
 51         if (rc != 0) {
 52                 fprintf(stderr, "E: bind(),%d,%s\n",
 53                     errno, strerror(errno));
 54                 return -1;
 55         }
 56
 57         /* 3. lisen */
 58         rc = listen(listen_fd, 10);
 59         if (rc != 0) {
 60                 fprintf(stderr, "E: listen(),%d,%s\n",
 61                     errno, strerror(errno));
 62                 return -1;
 63         }
 64
 65         printf("Now tcp server is listening on %s:%d\n", ipv4, port);
 66
 67         while (1) {
 68                 /* 4. accept */
 69                 struct sockaddr_in clnt_addr;
 70                 size_t clnt_addr_len = sizeof (struct sockaddr_in *);
 71                 memset(&clnt_addr, 0, sizeof (struct sockaddr_in));
 72
 73                 int conn_fd = accept(listen_fd,
 74                                      (struct sockaddr *)&clnt_addr,
 75                                      &clnt_addr_len);
 76                 if (conn_fd < 0) {
 77                         fprintf(stderr, "E: accept(),%d,%s\n",
 78                             errno, strerror(errno));
 79                         return 1;
 80                 }
 81
 82                 /* get info of the client */
 83                 char clntipv4[16] = { 0 };
 84                 const char *ptr = inet_ntop(AF_INET,
 85                                             &clnt_addr.sin_addr,
 86                                             clntipv4,
 87                                             sizeof (clntipv4));
 88                 printf("\nconnection from %s:%d\n", ptr,
 89                     (int)ntohs(clnt_addr.sin_port));
 90
 91                 /* 5. recv */
 92                 rc = recv_file(conn_fd);
 93                 if (rc != 0) {
 94                         fprintf(stderr, "fail to recv file on fd %d\n",
 95                             conn_fd);
 96                         close(conn_fd);
 97                         continue;
 98                 }
 99
100                 close(conn_fd);
101         }
102
103         /* 6. shutdown */
104         close(listen_fd);
105
106         return 0;
107 }

4. client.c

 1 /**
 2  * client.c - tcp client to send a file like scp
 3  */
 4
 5 #include <stdio.h>
 6 #include <unistd.h>
 7 #include <errno.h>
 8 #include <string.h>
 9 #include <fcntl.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <arpa/inet.h>
14 #include "libfoo.h"
15
16 int
17 main(int argc, char *argv[])
18 {
19         int port = PORT;
20         int rc = 0;
21
22         if (argc != 4) {
23                 fprintf(stderr, "Usage %s <server> <srcfile> <dstfile>\n",
24                     argv[0]);
25                 return -1;
26         }
27
28         /* 1. socket */
29         int sock_fd = socket(PF_INET, SOCK_STREAM, 0);
30         if (sock_fd < 0) {
31                 fprintf(stderr, "E: socket(),%d,%s\n",
32                     errno, strerror(errno));
33                 return -1;
34         }
35
36         char *ipv4 = argv[1];
37         char *srcfile = argv[2];
38         char *dstfile = argv[3];
39
40         /* 2. connect */
41         struct sockaddr_in srv_addr;
42         memset(&srv_addr, 0, sizeof (struct sockaddr_in));
43         srv_addr.sin_family = AF_INET;
44
45         rc = inet_pton(AF_INET, ipv4, &srv_addr.sin_addr);
46         if (rc != 1) {
47                 fprintf(stderr, "E: inet_pton(),%d,%s\n",
48                     errno, strerror(errno));
49                 return -1;
50         }
51         srv_addr.sin_port = htons(port);
52
53         rc = connect(sock_fd, (struct sockaddr *)&srv_addr, sizeof (srv_addr));
54         if (rc != 0) {
55                 fprintf(stderr, "E: bind(),%d,%s\n",
56                     errno, strerror(errno));
57                 return -1;
58         }
59
60         /* 3. send */
61         rc = send_file(sock_fd, dstfile, srcfile);
62         if (rc != 0) {
63                 fprintf(stderr, "fail to send srcfile %s to dstfile %s\n",
64                     srcfile, dstfile);
65                 close(sock_fd);
66                 return 3;
67         }
68
69         printf("OKAY - send file %s to %s:%s successfully!\n",
70             srcfile, ipv4, dstfile);
71
72         /* 4. shutdown */
73         close(sock_fd);
74
75         return 0;
76 }

5. Makefile

 1 CC      = gcc
 2 CFLAGS  = -g -Wall -std=gnu99 -m32
 3
 4 all: client server
 5
 6 client: client.o libfoo.o
 7         ${CC} ${CFLAGS} -o [email protected] $^
 8
 9 client.o: client.c
10         ${CC} ${CFLAGS} -c $<
11
12 server: server.o libfoo.o
13         ${CC} ${CFLAGS} -o [email protected] $^
14
15 server.o: server.c
16         ${CC} ${CFLAGS} -c $<
17
18 libfoo.o: libfoo.c libfoo.h
19         ${CC} ${CFLAGS} -c $<
20
21 clean:
22         rm -f *.o
23 clobber: clean
24         rm -f client server
25 cl: clobber

6. 编译并测试

$ make
gcc -g -Wall -std=gnu99 -m32 -c client.c
gcc -g -Wall -std=gnu99 -m32 -c libfoo.c
gcc -g -Wall -std=gnu99 -m32 -o client client.o libfoo.o
gcc -g -Wall -std=gnu99 -m32 -c server.c
gcc -g -Wall -std=gnu99 -m32 -o server server.o libfoo.o

# --- Terminal 1: start server -------------------------------------------

$ ifconfig eth0 | grep ‘inet addr‘
          inet addr:192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
$ ./server 192.168.1.100
Now tcp server is listening on 192.168.1.100:2345

# --- Terminal 2: start client -------------------------------------------

$ ./client 192.168.1.100 /tmp/client.c /tmp/foo.c
OKAY - send file /tmp/client.c to 192.168.1.100:/tmp/foo.c successfully!
$ rm -f /tmp/foo.c
$ diff /tmp/client.c /tmp/foo.c
$ echo $?
0

# --- Back to Terminal 1 -------------------------------------------------

$ ./server 192.168.1.100
Now tcp server is listening on 192.168.1.100:2345

connection from 0.0.0.0:45182
> dst filename=/tmp/foo.c, filesize=1532, filemode=644
^C
时间: 2024-12-14 19:50:06

图说使用socket建立TCP连接的相关文章

一个人也可以建立 TCP 连接呢

今天(恰巧是今天)看到有人在 SegmentFault 上问「TCP server 为什么一个端口可以建立多个连接?」.提问者认为 client 端就不能使用相同的本地端口了.理论上来说,确定一条链路,只要五元组(源IP.源端口号.目标IP.目标端口号.协议)唯一就可以了,所以这不应该是技术限制.而实际上,Linux 3.9 之后确实可以让客户端使用相同的地址来连接不同的目标,只不过要提前跟内核说好而已. 当然,你不能使用同一个 socket,不然调用connect连接的时候会报错: 1 [Er

通过UDP建立TCP连接

解释 通过UDP广播查询服务器的IP地址,然后再建立TCP点对点连接. 应用场景 在服务器IP未知时,并且已知服务器与客户端明确在一个局域网或者允许组播的子网下. 通过UDP发现服务器地址然后再进行TCP连接. (PS:万维网很多路由器对组播进行了限制,所以不能通过UDP报文在万维网上进行服务器查询) 主要问题 Android真机和模拟器对组播处理不同 Android不同系统版本对组播处理不同 不同网络对组播有限制,如部分路由网络限制UDP报文 简单实现 传统组播方式,通过255.255.255

python 批量执行脚本(服务端和客户端建立TCP连接)

有很多开源的批量部署的工具可以使用,比如puppet, ansible , saltstack , chef . 但这些工具都有自己的一套语法规则,你得按照他的语法来编写程序,然后批量执行. 那有没有方法可以直接写bash 或者Python脚本,然后批量执行呢? 方法当然是有的,需要自己写程序来传输脚本,拷贝文件,再复杂点,可以自定义脚本式同步还是异步执行,自定义要执行的主机组,等等,需要根据自己的业务需要来想. 这里只是抛砖引玉,我采用建立socket(TCP)连接来批量执行脚本. 服务端脚本

socket建立tcp、udp链接

建立不中断tcp链接: tcp服务端: 1 from socket import * 2 3 ip_port = ('192.168.0.101', 8000) 4 buffer_size = 1024 5 back_log = 5 6 7 s_server = socket(AF_INET, SOCK_STREAM) # 启用tcp链接 8 s_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 允许重复使用IP地址 9 s_server.bind

建立TCP连接过程

1.服务器实例化一个ServerSocket 对象, 表示通过服务器上的端口通信. ServerSocket serverSocket = new ServerSocket(port); 2.服务器调用ServerSocket的Accept方法,该方法一直等待直到客户端连接到服务器上给定的端口. Socket server = serverSocket.accept(); 3.服务器正在等待时,客户端实例化一个Socket对象, 指定服务器名称和端口号来请求连接. Socket client =

Socket建立网络连接方法

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket . 套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认. 1.服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求. 2.客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字. 为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字

建立TCP连接的三次握手

请求端(通常称为客户)发送一个 SYN 报文段( SYN 为 1 )指明客户打算连接的服务器的端口,以及初始顺序号( ISN ).服务器发回包含服务器的初始顺序号( ISN )的 SYN 报文段( SYN 为 1 )作为应答.同时,将确认号设置为客户的 ISN 加 1 以对客户的 SYN 报文段进行确认( ACK 也为 1 ).客户必须将确认号设置为服务器的 ISN 加 1 以对服务器的 SYN 报文段进行确认( ACK 为 1 ),该报文通知目的主机双方已完成连接建立. 三次握手协议可以完成两

客户端与服务端建立tcp连接三次握手之前做了什么----DNS

操作系统在握手之前进行了DNS查询   DNS 迭代查询 1.操作系统会首先在本地缓存中查询IP 2.没有的话会去系统配置的DNS服务中去查询 3.如果这时候还没得话,会直接去 DNS 根服务器查询,这一步查询会找出负责 com 这个一级域名的服务器 4.然后去该服务器查询 google 这个二级域名 5.接下来三级域名的查询其实是我们配置的,你可以给 www 这个域名配置一个 IP,然后还可以给别的三级域名配置一个 IP PS:DNS 是基于 UDP 做的查询, 原文地址:https://ww

socket连接和TCP连接的关系

我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP.FTP.TELNET等,也可以自己定义应用层协议.WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上. 1)Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等.而TCP和UDP协议属于传输层 . 而http是个应用层的协议,它