管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ k) u8 v! N# J0 [0 c
4 X6 a3 r0 ~6 w+ x Q. n1 x! J% @! f( g# u+ W. e# w
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
; A: r# u4 M" e7 @) `. c3 E
+ x& y: J7 m! _% v/ d1 M9 N$ ?& P2 D- @* f
TCP协议4 `# K" N* t( ?5 m
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
* I2 g* w/ k/ W5 |2 E5 w( e3 V* n! k W! b
" `. c7 D7 i" m. o c
关键词:三次握手,可靠,基于字节流。
8 F1 M7 v7 s7 K, o7 l" X
& W q) n8 @, `+ O% E. ]4 ]/ [8 r% r& m
9 k% I' ^, q! H! B' l9 r可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, J d; I Y: O
& c" `7 G0 ~- E; i" ?( y
TCP服务器端和客户端的运行流程1 V& F0 p: |" f5 u5 U
/ p6 `' D9 m" v9 x
) P, ?8 u) l" [5 i) w& d- u
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
, o$ P) A6 U) g* x4 k6 Y; s c! C. `5 {4 A4 Z9 x f$ g( a8 ^
$ r3 o3 m) R/ x. m. B$ d% o
1.创建socket, @* y' M9 Y; Y; B0 C
socket是一个结构体,被创建在内核中
4 C" P |6 V9 w0 G sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议8 u6 C6 O' j2 u8 R. X0 o. d
9 A1 Y! T& S3 W: V
* W& \3 n& N( k5 S3 `2.调用bind函数& o5 b' q4 Q e( ~" N! r
将socket和地址(包括ip、port)绑定。. B; K [; ~& T J9 i& d+ |
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" ~ S; o# `+ F# ~* d5 x
struct sockaddr_in myaddr; //地址结构体
! E C/ q% N" X, o- v" \; d bind函数( P; w& K' W) r( `- a5 K
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
2 ^4 w: u2 R, @9 i! G2 j W& T$ }3 Y0 i$ T' J! Y2 W' R
; F" e. T+ e0 l4 a9 G3.listen监听,将接收到的客户端连接放入队列
% K, j" P; b, }+ Z$ I: H+ V listen(sockfd,8) //第二个参数是队列长度5 c; [6 Z0 u+ A c1 w$ B/ O
5 N' n! W; r: U
5 r$ P& X5 [4 E/ x0 I; y1 U2 g4.调用accept函数,从队列获取请求,返回socket描 述符
0 h7 H9 @) J: l( A3 H, g# e 如果无请求,将会阻塞,直到获得连接4 W2 Q2 [# w5 l: ~
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
5 `0 e; w3 c+ i7 r
: B& ]5 I6 k' k) s+ E @2 @$ O. ]0 U
5.调用read/write进行双向通信
0 g6 U# ^; M9 q' i7 q& X2 j. R' r0 T* X; H: r& k* G* b( h
" Y J. r( m: w {6.关闭accept返回的socket4 g) h" F @& j1 i7 V
close(scokfd);: Z+ R8 y+ o- q; ?/ w4 c' I
) [0 n5 D8 {0 Z
4 S6 k, k# S2 z! d1 k4 h' j( f$ i8 _/ f
& A) u+ E8 u6 {& [
[: ]. U1 F4 Z0 Z' W6 U下面放出完整代码
4 r4 y) y4 V# P, C) {# V8 s) f2 C9 J* |
- /*服务器*/
9 m7 x9 X! ~: X* } - #include <stdio.h>
( {, f% e5 o. ^7 Y - #include <string.h>
/ W: Q5 J. a+ D1 q/ ^7 f9 G' Y0 V - #include <stdlib.h>' }7 F% O# ?2 t% E' W
- #include <strings.h>
7 W, T* ^# U1 d# U$ A% d+ j1 E5 J - #include <sys/types.h>' i4 m) Y" v5 b' ?3 E# n! h2 p: w
- #include <sys/socket.h>
7 y3 `" }2 @( z - #include <arpa/inet.h>
/ ~/ ^, b2 w/ g; _ w7 i - #include <netinet/in.h>+ Y& w+ }6 P- m
- int main()
$ D' Z2 |. S2 q - {0 c# t' A! q9 V+ E* k
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字/ J3 ~1 A, z: }: _( M" C
- if (sockfd < 0)# k7 B+ n ]7 m# h( B( Y
- {
5 |. e' |5 U- X, Z+ O* p7 v - perror("socket");' F" n8 @& X+ R \1 f/ C+ [ P4 J& y
- return -1;! t" d+ A2 S) `. {2 O
- } //创建失败的错误处理: j7 ^4 e5 U7 }" k6 e
- printf("socket..............
; k& ~2 D5 `0 M - "); //成功则打印“socket。。。。”) c; I8 m, y+ b7 s/ f0 @1 R2 d
- 9 V( E( @0 O+ V
- struct sockaddr_in myaddr; //创建“我的地址”结构体$ i+ ?% H* O3 c
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
/ @9 B7 D) O2 p U - myaddr.sin_family = AF_INET; //选择IPV4地址类型
2 U) D; U/ A1 d4 m4 y& `8 O, V - myaddr.sin_port = htons(8888); //选择端口号
) P( l- t1 g3 A- {- ` - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址3 J' r2 I; }: a1 K M
- 1 M6 l! @1 T5 d
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 k6 o" ]6 V8 ^
- {4 h" Z6 Y: a* j( q8 |5 x& D/ K4 W
- perror("bind");
K4 H$ F: d/ K$ b" m9 i - return -1;# ~8 d/ K8 I! I1 S! ?& O! {: |
- }
3 {0 g% v3 E w# d+ A - printf("bind..........
" l! s" g4 R2 A& n9 |: I$ B8 Q - ");5 Z% d; Y( G3 s. [2 w7 W
-
9 G2 Y7 @; N0 E0 a0 T' X+ V- I - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
* F& a2 H' }" k* v9 g - {
0 t1 F$ @& b" h" I* l6 C! @5 f1 g& K - perror("listen");# |: \$ W7 L' i1 l
- return -1;! H5 X3 u1 r- G- V5 }$ M$ b
- }' P6 A5 t7 Y) |% k
- printf("listen............
( Z* N0 J3 Z5 n1 a - ");: J' P: h7 t) u: G5 q7 ]* m$ p3 O9 }
-
- ^# \+ }. F1 H- T: [. } - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求& |7 n! S, B' X
- if (connfd < 0)
7 W& ]4 L& A& _2 v - {
Q. x& j0 F a - perror("accept");
8 F1 L& o8 x) u9 R/ \% [+ F: S - return -1;
( }" B4 W; I% F; @ - }& f5 [) u" x- h, N$ A* H# S" n$ Q. w
- printf("accept..............
3 ^. o& C4 l# {2 @; g9 d& L7 P - ");
5 N" b0 F4 [ f2 ?$ i1 q3 O - char buf[100];//定义一个数组用来存储接收到的数据 I( }1 C9 |9 {
- int ret;, N0 @7 A; t) A* F9 x( V+ n
- while (1)
2 b/ f! H9 M/ B2 j# B' C) l+ T0 B* P - {2 U! a* s/ X3 E: B" l K
- memset(buf, 0, sizeof(buf));: z2 E, ]* Q2 E+ I( H
- ret = read(connfd, buf, sizeof(buf));
3 i. S) L( L2 D" Y; c. J1 q9 j- E - if (0 > ret)# E% L2 ?0 z) x% R$ |
- {: \. ?5 \2 M/ J
- perror("read");
8 y" {# N. E+ Q1 k4 T$ A7 n3 w - break;5 R2 _- G* U5 H" A6 e/ f6 j
- }//执行while循环读取数据,当
% v' Y0 R. Q: g& v - else if (0 == ret)
# T+ Y( v% p' h, F - {; x- F0 V+ q: G+ Z
- printf("write close!% o3 l5 F# l- R: c
- ");0 K6 X: P) o7 N+ v0 \& R
- break; }# ~! N# r0 h/ N2 z# y! t/ T
- }
- R; o* D/ `: j @# g3 X. u - printf("recv: ");
8 i; u) j) ]! g$ m, p# X6 M - fputs(buf, stdout);//打印接收到的数据
- H% e$ Q S1 Y8 i, K, `; p - }
& e5 S! @1 T8 v G* U4 K - close(sockfd);//关闭套接字& a$ x0 b0 v. K$ ^$ R4 }
- close(connfd);//断开连接# c# G. e- v' N
- return 0;* ^: k# C; ]* V3 O& ^ l- m* Q6 l1 V
- }
复制代码
: Q; p5 r! \ \6 A, B M5 f8 r! x3 V3 j1 Z1 Y/ }& v
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
$ U8 Q" x4 D+ a - #include <stdio.h>5 r& Y$ X# A# n2 D2 u
- #include <string.h>% k& p4 l2 V% Y& Y, j- W
- #include <stdlib.h>, d8 ?/ T, N. Y: n0 r# y3 X
- #include <strings.h>
- k- F5 S3 e1 b7 b7 o" c - #include <sys/types.h>
0 S; b) X z0 V+ i+ X - #include <sys/socket.h>9 h6 {7 y: H' v% \" ]: M+ Q
- #include <netinet/in.h>
! e4 p8 F% `6 L - #include <arpa/inet.h>4 Q% p' r8 x0 b: S- O
- int main()6 H# s$ [0 e8 f; M' d
- {
' L0 U! ~9 K6 C+ g5 s - int sockfd;! Z/ u7 i6 T. O" G0 T0 D' S/ V& l
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 _2 @! [; L9 g6 j4 j
- {/ h( }) }5 s: q8 P. F+ ~
- perror("socket");
1 r! E6 t( V: u6 l6 P( y - return -1;
% U$ P! T' b) [& A+ c - }
6 H& p, u; v9 p4 y - printf("socket............ }. G9 a/ A E! ]6 `- H+ Y
- ");7 v. A& V1 }2 v* h! o
-
# |8 J7 N a+ o3 Q+ z - struct sockaddr_in srv_addr;5 v) I6 o* |& `" a7 Z; s5 @
- memset(&srv_addr, 0, sizeof(srv_addr));
* C; I9 n& b6 p, t0 V# N - srv_addr.sin_family = AF_INET;
7 _( s% B) Q0 w; b - srv_addr.sin_port = htons(8888);
& A1 @6 f8 Z' T9 y, e* J - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");$ ?; w3 T! B5 C1 P4 F" P. c0 G
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
2 i" A, O5 m% r5 K- K4 o - {
, j/ F. u3 }8 [$ l+ B! c - perror("connect");# ~0 N8 Z- z9 I3 i* T
- return -1; //exit //pthread_exit! m9 o" x$ I3 s, W: [3 u; {
- }( ?0 k F1 G# S2 ~' g
- printf("connect..............% L: t' Z: k8 C I
- ");/ e! v1 X8 _ D* \$ Q
- char buf[100];
) v2 o- ~$ j! {7 g: e - int ret;4 p# y8 l) S6 p; j8 X
- while (1)3 b! ^- `+ L* |0 I- Q7 }# X1 B, v& T
- {; P4 T4 o6 z4 l0 ^
- printf("send: ");
; d2 r& ]0 e- m2 ~ - fgets(buf, sizeof(buf), stdin);
6 L9 e* }( i( Z/ O5 F - ret = write(sockfd, buf, sizeof(buf));
% N2 {6 {3 q! c4 t2 J3 d - if (ret < 0)
' x6 L* ~) _+ @# |( [9 F) S - {
z: T' I( A6 e0 a# P - perror("write");
# p1 a- L* k8 f% J - break;( N% \5 N% A* e# g9 ?
- }" Q s/ m$ G: O( t- A# A
- if (strncmp(buf, "quit", 4) == 0)
& k/ H6 f {7 q/ B! x0 l$ y9 g* @$ b - break;
+ P# B0 r! o" L) a; j( C - }
6 R4 m1 L+ q& X! ?/ x - close(sockfd);& l" n, T& z+ g# [; [: Y- P& v6 U
- return 0;
: t( |+ \9 N3 ~" x J; C. j, Z - }
复制代码 ; [$ g+ W& c5 F Y0 r
% O1 q/ b% K/ @5 K- B
|
|