管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
6 Z) n9 ^& m. l- i! R- ]6 ?& e2 H% \) Y" w
! [( F8 n+ Q3 U/ h# ~# Wsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ }+ [$ `$ S1 c6 Z: f7 {
5 K$ }/ V8 {1 M* P$ _# B7 |
, y/ I5 e# v# v5 q1 u* v) b. `9 g0 dTCP协议: t; w. o6 T- `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
/ N# P6 w' @& i w$ f" q! N* h$ ?4 C, a% |3 X6 L$ t
( ]6 {, x w4 W; Y. g关键词:三次握手,可靠,基于字节流。
: o, `" _; D( K9 t% O6 N" Y# A$ D( z' ~1 a1 R! J, c7 |
7 |2 ~- A, t1 j& s% h
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
I; ~% X/ }1 A
* C; J# S! j5 hTCP服务器端和客户端的运行流程. W5 F, N; E+ F* p3 T R
b/ r6 l# G V. P. }
- o( [& G& [0 d如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
' Y3 L! h6 w4 {2 f
6 T; j! ^: t& Y: B# O) U7 T/ H6 r- }2 c, k; S
1.创建socket
2 e8 }" G" O' m socket是一个结构体,被创建在内核中! ?) J+ x1 k& X1 W& `( e0 ?1 y1 T
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
l* v0 e( l# u5 B
+ Q, s: u9 V; v! z. S( ]+ H+ O' A
0 N7 |1 O& h0 r) P i/ i8 z& O2.调用bind函数0 e" ^: F+ t5 {' X1 w
将socket和地址(包括ip、port)绑定。
7 u4 X; P; b ~" `0 z( Q. D- ` 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序# {) U* r3 |4 O7 u3 Z5 V% u9 D! P5 ^& _
struct sockaddr_in myaddr; //地址结构体
+ S) s, V. Z6 B- D8 d ?) N6 Z bind函数# W6 J6 |. a* ~5 n" J+ G
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
& T8 ` T `* j( r) v
; _& O3 C7 v# i- d7 c/ |
: ?- Z1 E( p+ `5 W P* D" H3 q3.listen监听,将接收到的客户端连接放入队列3 n2 J* G( W/ M: i
listen(sockfd,8) //第二个参数是队列长度8 K4 M: K+ m; e: _/ T7 Z7 X; M
7 y. J# Q& E- E; m3 b* r
/ M0 \* b' R0 B0 K% c
4.调用accept函数,从队列获取请求,返回socket描 述符
5 \# h0 |4 ^/ X 如果无请求,将会阻塞,直到获得连接
& {$ ?: U/ ^+ G( A: M int fd=accept(sockfd, NULL,NULL);//这边采用默认参数% m* J9 d4 l9 h7 E+ ~7 g
$ C# D# n. i4 y [
5 L8 i# h" p: W/ h5.调用read/write进行双向通信( q6 n- `. u( o+ H+ P. f- P) h# {
[8 J) Z2 l- l; h# T" H. N- M& |
7 c% G: q ^8 [
6.关闭accept返回的socket
E. @( M$ f; S6 R0 Q% q close(scokfd);
I( q) H& s6 o f$ N# z R8 A, a0 \% G* |7 Y
! X8 h0 K j" e4 h- j
3 h2 I% e, {- A" u) t5 g
( W4 |& I0 L& i( @
下面放出完整代码' Q) m$ {- T0 s. |, }* D
, V# M) m2 W8 M9 [
- /*服务器*/
( i' s( }9 R0 r2 B% D6 d( V4 @, j5 g - #include <stdio.h>
9 I( S; V! C7 ~0 E - #include <string.h>8 { g; q! s2 D- ~. g
- #include <stdlib.h>& ]. f8 e/ I7 e) ^% `
- #include <strings.h>
% D# I' n: u# q& _ @ p - #include <sys/types.h>$ j& S- q8 U g6 _4 l; Y. _# r& F
- #include <sys/socket.h>
2 u3 o' V# x; J8 o" ?0 }; c s' L! m$ {, V - #include <arpa/inet.h>
# G* L. r! t; ~ O/ g0 S+ { - #include <netinet/in.h>( K- j- b# S7 A6 p( I# i/ T: t6 N5 p
- int main()' [. E1 n* L' q" J
- {
8 ]1 } m2 h& K- z, f" Y - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
0 S. a1 p0 w* z. s - if (sockfd < 0)
& T$ t% F5 C; i% {. _: }' T - {* ]; T+ N1 u# V! u5 e
- perror("socket"); E* h( o* K1 d8 x8 Y
- return -1;
- |3 s' _) a$ p; U. k - } //创建失败的错误处理: j8 y% W! m3 o$ W1 ~/ G
- printf("socket..............
/ t1 i$ q8 N. i( B4 H A( T - "); //成功则打印“socket。。。。”
6 J h9 C9 }0 q3 X -
6 L; N/ q) Z5 @7 G0 e - struct sockaddr_in myaddr; //创建“我的地址”结构体% M0 R% P; h) K5 ?& u
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% D2 O5 P# b9 ]) i
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
( `% h: G& K& K( D- f3 D - myaddr.sin_port = htons(8888); //选择端口号
# x$ X4 r( T) O! t, }; |5 @ - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
- q* C2 |8 j1 y* e -
6 V$ Y9 r( \& ]1 Q0 _ - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字; v- n0 i: m. z0 k0 E1 s. X0 K
- {; e0 y0 n# L1 n' @- p( A ~; [8 t
- perror("bind");
" J d- I$ r- l4 W$ Y - return -1; I* h1 S" K, J/ ~, `* ]; n
- }' I& W8 S$ p% E5 D; c. I! Z
- printf("bind..........4 D* \7 Z: O% p$ ?$ U# s
- ");4 q L- x: w+ r
-
- Q; F/ |2 V2 X - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 f8 ^4 p C( D& z1 g
- {
: j3 I5 S1 l3 W Y4 L' ^. a - perror("listen");5 X) \, ~. [8 X5 c! _% ]6 s
- return -1;& k0 I3 ~* A1 h0 M2 d5 m
- }
; U, s! Z0 n& a* P2 \% I$ l3 A - printf("listen............
+ e. O' v. V: {7 I% H - ");7 ]2 k" g6 P! u
-
2 c% H# o3 T6 Q$ d4 d3 l7 n - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
! L/ p3 I$ s( W; z8 @7 b - if (connfd < 0)
$ l* W( M* G- v8 X% s - {) o; X+ E+ w5 g; r7 a# l
- perror("accept");, ?- q, s* \/ Z
- return -1;
6 q' ^6 c3 C7 Y' q, C - }: X7 W) n5 B6 R$ ~+ u+ X3 }
- printf("accept..............6 Y& y( O0 Y: R
- ");# A' b3 Z$ q& ~2 G7 r
- char buf[100];//定义一个数组用来存储接收到的数据
2 ^1 ^6 o( Q' U - int ret;1 N8 X% X7 V" W& C. \2 j
- while (1)
9 i# Q- w& K7 p - {& s) |: a: J. F; D0 R/ G
- memset(buf, 0, sizeof(buf));
0 X( b( X: L- {7 h, \7 n' _3 i3 p - ret = read(connfd, buf, sizeof(buf));* h1 n6 G: ~5 v5 R p# Z# k
- if (0 > ret)& O0 o4 s |1 f1 O1 {4 T
- {
6 l* `" q, |4 S( H, U' k - perror("read");
7 W" X! K- V: J - break;* I" @* p8 K3 |$ q8 ^4 j7 \) @5 [
- }//执行while循环读取数据,当' S8 j q' p% x+ _
- else if (0 == ret)
9 J& P0 F3 V* w/ b - {
9 S$ Z! q# W5 B2 B6 T - printf("write close!
1 A- r4 m t$ ~+ ] i - ");
7 A1 C/ m8 c! d+ m& K - break;& X" x, u# V" a! M* H2 y8 L
- }' [2 a3 e. T6 h( c7 }" _/ A( j
- printf("recv: ");: S. P) \3 B' g1 _$ D" y
- fputs(buf, stdout);//打印接收到的数据
1 D! _' ?4 j$ K, Y/ K/ s# X& x - }5 h0 r8 Y" O$ ?2 M6 l3 E1 l
- close(sockfd);//关闭套接字" w( G/ o) m8 E- d
- close(connfd);//断开连接
; I. B% Q0 @- _6 F, O - return 0;+ `- a* v% W2 a9 z& P/ a% T
- }
复制代码 % R0 e) a- k0 F0 {% Z8 ~
: S9 p2 j: [! Y" [- r- /*客户端*/(具体功能和服务器一样,所以不再加注释)
+ n: r9 R4 z7 G% G9 r# F% |# c - #include <stdio.h>6 }3 p6 g9 o; q3 k2 \) e* r# h, L
- #include <string.h>
/ Z+ f7 H+ p' }1 t' z - #include <stdlib.h> w9 p0 O& o+ P1 E. E
- #include <strings.h>" T O$ m z4 c! R0 ^+ y
- #include <sys/types.h>- J6 z' o: F& x4 l4 K
- #include <sys/socket.h>
! y; @ x. F1 |: z. m7 \3 w$ W - #include <netinet/in.h> S, l: N/ }: y6 E6 S7 H" B
- #include <arpa/inet.h>
* j3 z: \$ l5 ]' V - int main()
# c m" q2 G! w* X - {, }% R7 i( I, @3 R" r* i y: r
- int sockfd;
9 V/ m @& W/ M( [$ \! w. s - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))). \1 e" G9 a0 D1 M. Z
- {
$ R& d3 p4 _- I( n - perror("socket");
+ r' _9 Z% h7 R) N6 q: Y( d - return -1;9 Q" P c8 c5 d3 ^( Z9 l
- }
2 K0 I* B# T7 Q- \7 I3 x( p - printf("socket...........9 \# C6 v* W2 N: i1 A
- ");
. u r+ {) b ?8 h# k# n -
( W8 {" |6 C/ I( _% | - struct sockaddr_in srv_addr;
; F; n' X( V+ J- h' Y3 g4 R: C - memset(&srv_addr, 0, sizeof(srv_addr));5 G+ h! ^. i, `! N, d+ g1 b% z9 }
- srv_addr.sin_family = AF_INET;: P2 f6 }, s( d7 A
- srv_addr.sin_port = htons(8888);* L' o. ?: G: Z, u0 J4 f w. G
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");' v3 ] s) Z9 C8 b9 M6 h
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))! g' E4 \7 S6 p: Y; \
- {: B* R5 H: r5 e$ ^) D
- perror("connect");
2 q. _( n! A$ e) Y ] - return -1; //exit //pthread_exit v C8 B0 Z; X$ }6 F
- }
0 V7 V# y* m6 b% M - printf("connect..............! k: E8 F& s9 ~7 x4 z+ g
- ");2 D$ D* i: `/ Y1 A5 _! C" K. a* q9 ~
- char buf[100];8 b) {" E3 U5 f1 O4 ]
- int ret;
4 \# `0 s8 }7 B4 Y: W1 I- q - while (1)
* i* v7 F; H; ]7 c6 N - {
- w' f2 b, {3 u- U4 m - printf("send: "); t: X- |2 P2 p! L4 l: @5 U: D
- fgets(buf, sizeof(buf), stdin);
1 \& o$ p- q* y3 W! R1 `# w& r - ret = write(sockfd, buf, sizeof(buf));
; @9 X* \' f, d - if (ret < 0)7 n5 a! G7 H$ K0 r' S- ^0 z# \) ] K
- {
" l6 U% ?8 g, F. S( x: S* w - perror("write");9 D* l* ` H& }" e, N- \! j3 a
- break;
3 r; D( j# D5 S& f( O0 [ - }, L6 @4 _1 l1 u9 [# G2 }) u& ?; s
- if (strncmp(buf, "quit", 4) == 0)
+ W& R9 p$ y8 s4 Q - break;3 c: t$ L. X$ [$ C; u' T- Y
- }' N6 R. w: b$ t
- close(sockfd);
% l! T8 [$ q+ r - return 0;# c2 m& E w* g, z3 @
- }
复制代码
. u. d E- H; G( B* D4 x8 B/ x/ [) l. ^( {5 p3 L" g% R
|
|