管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' K/ I: Q9 I u3 K0 P3 `
7 u; D/ W8 m* b0 g# Q' d# B/ o$ I
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。0 k. c* s) R: I4 p% b
2 t, g9 t* M+ O" _2 w# z+ X( E
2 j: g( n6 k$ f7 w4 wTCP协议. y# |6 Z+ ]# k# t4 k8 S7 m$ g0 F; [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- B3 n2 U# o2 t' ^1 X; ~" K" ]4 l% B7 h5 d5 Y0 ]1 [
% F8 C ^4 R/ C9 q$ O- y5 O! M' ?; ?( G
关键词:三次握手,可靠,基于字节流。9 N$ F# I6 f1 G5 T2 b
) Z2 s, l( }4 q: O _- l
3 x& _+ W8 h' R' U1 Q
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 y+ q7 y, A) z: [: F
! T u% e8 Z/ a, W+ J# u1 }' HTCP服务器端和客户端的运行流程 n# y0 W7 N" Z$ s# p
9 H2 N1 I! e7 r# ~, v' H' A
5 b9 S9 T) [9 j如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 n' J, Z- l1 G+ C- B4 X$ x
% Q$ V% S" X4 g5 m/ K6 w
/ | W7 Y% a6 Y3 F: J- j, h1.创建socket; \3 r, D3 S/ i d3 O
socket是一个结构体,被创建在内核中
3 y% a1 |, S; V9 j, f sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 T: o+ E! Z7 D- U
5 f. |8 Q/ H6 L& A
7 O8 f/ t/ N* ~- j S! _2.调用bind函数7 @4 J; q5 u e: q
将socket和地址(包括ip、port)绑定。
' A6 A/ d2 h( g S; h$ ~' ~3 m 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
$ k$ H, A0 p3 I- t struct sockaddr_in myaddr; //地址结构体
: H& o/ w; P5 H bind函数& G, x# M& e& E/ Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 ?$ q8 E/ d" K3 w( [% v3 i" Y7 [8 p5 Y6 R/ b2 r' O
' w# A: a1 }7 G' p* q9 C. W3.listen监听,将接收到的客户端连接放入队列
' g- \9 h1 E n6 h' I listen(sockfd,8) //第二个参数是队列长度
/ h& X# e1 M+ o" z
2 q; q' Q6 l8 w/ e5 S
" ?0 s: i/ u. B* a X4.调用accept函数,从队列获取请求,返回socket描 述符
. a( I/ Q/ E1 B) Q# E. q7 Y 如果无请求,将会阻塞,直到获得连接1 z& n& s$ A( w! s
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数3 z) G1 U: q' R5 @) w. j
; N* F& w% q' s
" z/ }) H: {7 d2 Y" C5.调用read/write进行双向通信1 _6 G. P: u. o. j, |
4 g' `+ L+ Q! u" k" @* n/ T/ D [) _$ o, l |
6.关闭accept返回的socket
5 _6 o9 C7 ?6 I0 ?6 {# q, l8 F close(scokfd);7 W8 e) t! m8 E) P
0 y+ c$ T2 U X- C6 F' U0 K& Z+ f) H% A3 G
/ W2 | y/ I" L
$ p- R7 z, O& O0 @
下面放出完整代码
4 _" L$ W5 k9 w5 ~' c C& d& F! V6 D* `7 K4 N6 r- ^
- /*服务器*/4 Q" L& s$ Y# a( y+ y& a
- #include <stdio.h>
0 P' K* f T- ] - #include <string.h>
h8 \" E. o! a6 x2 _ - #include <stdlib.h>8 I8 [5 X" `3 y6 T" y
- #include <strings.h>
1 `- q4 j/ Y* \7 D( X, {- D7 m - #include <sys/types.h>
4 m7 o" F/ u6 [2 e% E - #include <sys/socket.h>, B7 V: a1 X' _7 P6 b: m7 T& d' Z
- #include <arpa/inet.h>
) x8 ^3 K$ v4 Y3 ^4 T9 I - #include <netinet/in.h>% E' C! j' A* }2 R) N: T
- int main()
3 Z% X4 z' y) A( U" \ - {
* E( r2 k- P, b- e4 f; d( U* ]8 o1 P - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字/ z8 A; i P# a$ X5 p
- if (sockfd < 0)( c# }- k8 S, `+ k. P' N
- {0 Y: f! _$ v3 t; N% R
- perror("socket");
g6 V0 u. F/ O3 y* H - return -1;; j7 Y( q5 H& s% ?, O" ?( i
- } //创建失败的错误处理, q: }; q+ V5 y$ X5 L9 D) [
- printf("socket..............
5 k# B2 V1 ~1 N( c - "); //成功则打印“socket。。。。”5 o& V* n$ A) y" b) d5 x( @) }( ^
-
* d) }3 I+ W( E( ~ - struct sockaddr_in myaddr; //创建“我的地址”结构体" D5 ?- c0 |1 Y* M/ g! t$ ^- G
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! F3 e* Y4 E8 r0 E' u6 m2 F
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
$ N( q1 m* X/ m4 j1 Z/ R2 o6 n - myaddr.sin_port = htons(8888); //选择端口号. b u8 r+ U/ Z
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址' a4 f+ d1 m; T! q
-
4 Z3 b0 [0 a7 l# a' q, y7 y5 m, c - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
# L, @8 ^, e2 s7 _& F# z# t9 ~+ u - {
) L6 W+ y, r: B - perror("bind");' p+ B+ |% C& _8 B5 @2 ~! [5 j' D
- return -1;
7 ]6 X6 A, e$ o$ d) [ - }/ K0 h. e$ N% I' L! p
- printf("bind..........' [0 C( `% G' T3 n
- ");
. m" e' q: E1 Y' Q ` - , A* [- j& W2 o6 G$ v7 {
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* X% u' U9 ]* N. ^
- {+ u5 C# \8 y. r1 Z; V
- perror("listen");
9 W; X( B8 ^7 Y( e3 P - return -1;6 v3 c4 E2 E5 ^# ]* |
- }
) M3 |7 S& M8 U; u - printf("listen............1 {- v- \% G8 h! A8 [
- ");/ M6 j0 a5 \2 T y
- Q" [- }5 D& o* e1 z$ d. w5 c
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
5 e& e2 f, f8 a - if (connfd < 0)
+ L3 t2 M4 m3 K) w$ v# _: G7 a - {
$ ] z( r6 k+ v$ y2 s# p8 v- Z - perror("accept");) T) x- T% |# L7 e
- return -1;
& d1 G C: [& q& D L" }% d - }
/ p2 O& ^( Z2 J0 C0 k; ^; ]1 G& t! U - printf("accept..............- T9 M1 R: ?# H$ y/ l
- ");1 ?" k: a7 S, i
- char buf[100];//定义一个数组用来存储接收到的数据
, q- u* w4 m+ ? - int ret;
) O% l, }" h* ]; V( w1 \ - while (1)# s* m, n9 }* j
- {
+ U8 ~% D7 f3 W1 @; M- \ - memset(buf, 0, sizeof(buf));8 J, S% Y% j( v& L( R( V" Y' L
- ret = read(connfd, buf, sizeof(buf));) U8 R% S- R7 v3 O. p( ^/ G
- if (0 > ret)9 c, F* \; Y1 i' }0 Y
- {7 G6 x* I& l3 z$ S+ E
- perror("read");
3 J% f) v$ e* C( n" O5 A - break;
: ~/ p! U3 w( u4 b- D - }//执行while循环读取数据,当* z+ _* N9 }, V( A6 D
- else if (0 == ret)6 R0 q" |; |. F- f: e, s3 H
- {; q0 B. L% C- g4 n" s
- printf("write close!
! c% C) N2 U4 d3 ^' H - ");# e5 ?4 q4 O, e
- break; _0 m! V. Q/ H% c+ \6 e: x
- }
/ q% M, T, B/ H l- D1 _. f2 { - printf("recv: ");' I/ S9 C$ Z, o
- fputs(buf, stdout);//打印接收到的数据
1 m; D& j! K' N8 h: \0 L- \7 W - }
& V+ {, j( I% B7 t" ?; @1 Y2 L& R - close(sockfd);//关闭套接字
& ]) Z4 @2 a2 e5 M5 k - close(connfd);//断开连接
- q# W8 @4 E& A - return 0;
/ z. I1 K' o1 i* w7 a4 x# i - }
复制代码
4 r/ v% S! J( x% ^8 C# J5 G5 W1 ?* ]2 e: }6 U( E- t
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
1 \7 u5 ]/ }6 F$ I9 `: t0 H1 {7 ^ - #include <stdio.h>5 y/ \( i: e ?, g" h* K2 e
- #include <string.h>- e" A5 r5 u/ d: R1 k# V' j
- #include <stdlib.h>( C, F# L! j0 R
- #include <strings.h>0 N, Z7 N1 i$ t$ X
- #include <sys/types.h>8 ?' B$ [. a0 Q" e' a% Z/ Z
- #include <sys/socket.h>* l: s8 i9 E) Q; Z
- #include <netinet/in.h>' j! ?( N" K( o4 Z$ m" s# N- ^- d
- #include <arpa/inet.h>
, c2 E4 k7 f2 I& @' y8 p - int main()5 K! o7 L5 i: Q& p* O5 l$ z# Q
- {, w6 I- x" l# c/ r, f
- int sockfd;
0 m* ^' t' s1 d, p# l - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- P" [3 x4 m! x$ f. I
- {+ _- @8 [5 z3 H6 i* E0 \. F
- perror("socket");! n) Y8 }! I q0 i
- return -1;6 d+ [! w6 g$ i. D9 `9 F
- }3 B9 S6 c& h6 _- m) T% n8 n
- printf("socket...........
% O+ H0 s6 ~+ D( E# H - "); e# }8 e% `+ T$ c8 |0 ~
- _! b. l6 B9 k' `( T& m9 J
- struct sockaddr_in srv_addr;
( u8 _- o5 w. m2 K1 A - memset(&srv_addr, 0, sizeof(srv_addr)); J1 p3 ]8 d) o: ]4 ]! S3 H
- srv_addr.sin_family = AF_INET; p! k, R6 r- l" n+ @4 p
- srv_addr.sin_port = htons(8888);
/ |4 [% D5 v- V( \ - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
+ K% W5 a8 c& A0 W* J2 m$ T( O1 W - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))1 t% C3 K, s1 n4 z5 X8 m5 `
- {
. M/ G! t/ p# ? - perror("connect");
3 R, D8 L! k6 |- b2 i) a" t- f) z4 W - return -1; //exit //pthread_exit
) k# `2 t. _) ? X' S0 Y - }
: q5 W: x& n* D0 L9 a% Y - printf("connect.............." R6 J. Y( `' K$ F( E2 h8 r( \9 y- S
- ");
. p2 x. K! t9 Q% V# Y - char buf[100]; ?4 o7 i6 F; [9 O
- int ret;6 ^; z, h' E! d% h
- while (1)
1 F6 ]3 o% L- b! x: h8 M+ Z7 m - {, ~) z7 M3 Z( d9 I6 L D9 _! K
- printf("send: ");
, e+ V2 A- e" n - fgets(buf, sizeof(buf), stdin);
8 C( R4 R, ~9 Y8 x# K - ret = write(sockfd, buf, sizeof(buf));- H# p* ~$ q- H6 C4 h" Z( U( E
- if (ret < 0); P. \* H0 F7 \# O' C
- {9 J9 J4 @( ?6 @" c) Y# p
- perror("write");9 B4 r4 Y6 Q h Y" \, d( K3 N! F1 o
- break; n) [+ V2 c2 C: E6 N8 w) f
- }
$ {' u9 n! k' `5 G* K9 U! {* t - if (strncmp(buf, "quit", 4) == 0)
# S. ?+ b% q& i9 f( U! x$ q, L - break;
9 @# [5 T- f7 @5 u0 ~8 H. P7 z( ] - }7 G* f- L5 z0 }* o7 n
- close(sockfd);
4 B' D! P0 j* a! O p - return 0;
8 ~' a: ~: I8 q, P4 z$ h - }
复制代码
: y" ^0 i7 a7 e1 ^1 n9 h6 J, \( W+ r& ?0 X8 M4 i
|
|