管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。4 Y& o" ^1 Q9 x% ]; k |" w7 R
' z5 o, k" V; S0 `& E) X$ }7 F3 X$ o) b
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ @6 {" z+ |, }$ ]; ~! q
4 P% _& Q( ^! F4 d- |4 h* z. g q, G3 f
TCP协议
$ p/ t) b; i4 Y$ X U7 N3 iTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 E& V2 f" o! V+ f D. k5 G; h
1 a, Y, z) h" i3 p; q; @0 z: B& K) `
关键词:三次握手,可靠,基于字节流。2 ~" ?9 k3 t1 b8 o0 ` `8 i
, B1 D0 H5 h, ]- P
/ L3 K6 P$ M) m5 W$ w. V+ r可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
! ?$ E3 @- v" |3 l
: B" r- L) [, v( V6 UTCP服务器端和客户端的运行流程) f0 E d; K! N0 m$ A
- l }' C( L. a+ Q' Q" n* O# N, ~1 y- c+ u" `; ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢? R0 w- D: u" b
# g8 @( t7 o4 m
! J" J! L& s$ n( n1.创建socket7 d; J3 `6 @! k. q2 U
socket是一个结构体,被创建在内核中
) A; O5 P! `8 J, W sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
% C7 O' e6 Y/ s2 @
8 l, u2 ?8 Y. o7 Z9 u3 u) f2 g2 t. [* ^
2.调用bind函数
( D" a# \, Q! J! A 将socket和地址(包括ip、port)绑定。# M" g1 V7 I( M/ G0 a* [" ?
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% `- G- _9 o$ ~# O6 h. Y" n
struct sockaddr_in myaddr; //地址结构体
8 e/ f" j6 v0 c- ^4 R2 j bind函数7 z( m& _4 i( n* ~- [
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 b6 j |6 s& H- D9 {5 A; `4 q+ a7 L
! ]3 _/ C; K/ P" X, x
3 A G% p- y( h3.listen监听,将接收到的客户端连接放入队列
. T2 `- |3 L( r$ C3 V5 K listen(sockfd,8) //第二个参数是队列长度
7 j2 D! m3 f! J
( e0 V2 \+ U4 W- J+ d
6 d, Y! k" d( F& X, a4.调用accept函数,从队列获取请求,返回socket描 述符
/ a% o, ^* p$ @4 K, G) o. \ 如果无请求,将会阻塞,直到获得连接4 `- C! E% P: P" Q) ]
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' @) x5 t; R3 t
I. P8 l- J8 }1 P
$ k; f* @, w+ y: h! k: s' v: F5.调用read/write进行双向通信
3 o% E2 c/ v- \) x ^% T8 |0 K( B0 n1 k% F# f9 s* H& t
& x' T7 [( V- \( y0 \9 ]6.关闭accept返回的socket
6 _( q6 M9 I. f T3 r close(scokfd);
) p3 ^; \: H% C( e2 Z) m5 W ]" e* _
. I+ |) X0 X5 g# G8 l1 H( B
* ]9 C4 ?% ?* H) D
2 m( j" p" y8 T2 n0 \9 A7 ^
下面放出完整代码
/ a: m1 O: N( P% k$ j- ^ y' n* r2 H; D p: M
- /*服务器*/$ k' ?3 G) ]6 |+ _0 ^+ j
- #include <stdio.h>5 I# c- T/ m; r) f( K- i# A F
- #include <string.h>1 N. u5 v6 O& J7 p% E% `
- #include <stdlib.h>1 Q4 f6 s# q2 D% S4 T! D2 h
- #include <strings.h>* O% S7 e1 S Z
- #include <sys/types.h>
4 Q3 ?0 O% r8 X& r( m! { a - #include <sys/socket.h>: N5 {2 d; a5 x: H& k
- #include <arpa/inet.h>
w6 l% N! z% h. N( ~: x - #include <netinet/in.h>4 t; n5 n' i/ C p: F
- int main(). F. A% g! ~% d% B4 S
- {0 s: r6 f( s# I2 N( u' c$ P
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字' l" {, P/ `, W! P# j
- if (sockfd < 0)
% L- ?+ {& j1 P4 D" h - {
- l2 @9 U4 r$ w7 M1 a% n$ t) b/ n: u - perror("socket");2 _4 t3 ]5 ^0 d
- return -1;
+ g& d+ Y& S7 a+ f8 E; U - } //创建失败的错误处理* h* [- W3 v0 U8 ~/ a0 o* Y
- printf("socket..............
& K; u# x5 C5 |; x2 o2 v - "); //成功则打印“socket。。。。”
3 Q" v6 e4 S1 O" I -
, K1 e+ K' l+ f1 A" O$ E' B. Q - struct sockaddr_in myaddr; //创建“我的地址”结构体. \$ z' S, x; \( F- c
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 O8 I: w; C5 P5 W3 V; L5 i
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
& y# H: a8 d5 ^5 q3 E/ E5 N" H - myaddr.sin_port = htons(8888); //选择端口号4 @5 \) f! T1 k1 r) y
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址) e0 w; Y, h' C3 }/ `8 D
-
. s& H6 C% s8 w0 z5 v* F - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
" Y. e) r, Q( F( I# w$ M! J: w* R - {
0 k$ v, Z4 i& N- g - perror("bind");
# M! M- k! z; H4 o# s: ?1 @ - return -1;3 o8 T& u/ N! X! _
- }
! x6 M; Y! D6 p6 u - printf("bind..........
5 ?$ v5 Y2 i) {( v0 p - ");
2 h$ m* G. B! Y2 B, D" C9 w - 0 R1 o8 c% R0 c% `) H$ [
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听; V% y9 Q G- i2 @1 W# i
- {, o5 e4 U: B( i' k8 R1 i8 [) Z! D
- perror("listen");6 E+ o: t1 H( z
- return -1;
/ \1 p/ u6 g) N' x - }/ j# Y8 h2 D1 Q+ B( b
- printf("listen............$ k* X/ j0 ]0 {/ k! W8 c7 J6 n
- ");
, [/ i( g6 ^6 m# P6 G9 X - % ^) j6 S7 R" g" v
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
8 P0 p9 W9 f& [2 c, l/ b. h - if (connfd < 0)# G4 W7 `# i$ f# t1 l
- {
1 M# N% p: p! G# e - perror("accept");
, ^0 V& ]+ l# C! n - return -1;
5 X k* Z$ i& T) ^, X - }
+ S w& |+ o$ x( C( {+ y - printf("accept..............
& t3 s- y4 u! ]! `& k - ");
3 l; N7 @9 F5 w; V: N2 y - char buf[100];//定义一个数组用来存储接收到的数据7 h0 Q% k0 m- R- Q8 Y
- int ret;. x: G8 [3 i* F
- while (1), W$ E$ r. G# e& X
- {
9 J8 }% z2 ~. F& m- t* Z - memset(buf, 0, sizeof(buf));
! Z1 s R+ \8 k5 J- y% K - ret = read(connfd, buf, sizeof(buf));
6 J3 r) `4 D' Y1 E - if (0 > ret)
( z2 c# @4 @% a7 H- | - {) e2 |6 O0 {6 g* ]) ]6 a
- perror("read");
* o& h2 K) n. e' i" ] - break;( @. Z/ X4 w! N. B
- }//执行while循环读取数据,当
; W3 W% z! B$ R9 g, D9 W - else if (0 == ret)3 v, l+ ^' _ M2 ]; ]
- {$ [$ a, R2 X, C' `; x
- printf("write close!% a# J& Z; B. x, n% m" N p) u j) o
- ");- g, p% @ v. [
- break;$ Y' j$ ]9 L" l( `; E
- }
7 f! C8 _8 |( T9 S! ] - printf("recv: ");
! o- J# o/ B! N- I+ G - fputs(buf, stdout);//打印接收到的数据2 |) u4 y$ D h8 R' B& _, u5 O
- }5 p/ B. z9 C6 i' N! ], C* `8 y7 E, i
- close(sockfd);//关闭套接字& @1 m& n7 I$ G y9 I6 N
- close(connfd);//断开连接1 p- [$ o" N( T! s! b5 P: Y% p' T
- return 0;2 w2 p& }1 u( Z* V
- }
复制代码
7 ^. h( N" T5 a, d7 z0 s+ r: j* m/ p$ C4 T
- /*客户端*/(具体功能和服务器一样,所以不再加注释)* m3 V$ `, i% k9 K l
- #include <stdio.h>
2 R2 B9 ?& _# j4 Q% S4 s. p - #include <string.h>
# S# w9 [4 A1 f - #include <stdlib.h>
4 h: M/ i0 {# E z" e - #include <strings.h>7 v4 a1 K% v8 j! s: a8 H
- #include <sys/types.h>
! s S7 d8 Q1 R0 x$ { - #include <sys/socket.h>
9 c4 m" c# p" W: a! H6 M; _ - #include <netinet/in.h>
8 u% A* G$ D- t0 V, U" n; j' X4 n: G - #include <arpa/inet.h>
7 K1 [4 p. T/ l! n9 V1 W - int main()
. h# s* h9 d2 ]5 w/ \* o - {
8 k5 b! S1 |* F8 ]/ F - int sockfd;, ~6 d+ Z% e4 |8 | ^# h
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))): J' T7 `: Y& r. t, a) ]6 |- w* G& ~
- {
% ^8 U' A* @. H2 z - perror("socket");' g- K h/ A! y# H, L
- return -1;
9 k7 l1 D, ?' I( I, B - }
% I7 q5 W8 \* _0 w - printf("socket...........
8 M. k/ c3 ^0 M8 s% @$ `; q7 i$ I - ");8 s6 Y, R6 Q$ b% f- V2 X
- 1 x9 L5 B, b+ h& u" v/ T
- struct sockaddr_in srv_addr;$ Q; C& } k2 _7 O$ i
- memset(&srv_addr, 0, sizeof(srv_addr));5 k* F) T0 I# n m5 C( M; d- b
- srv_addr.sin_family = AF_INET;6 [0 k# T3 `- S( r# q+ {
- srv_addr.sin_port = htons(8888);
5 h L% Y. Y- Y6 y - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");2 ^% Q: ]6 r8 L0 e# k
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
. s# j! [& r$ |$ ^$ r - {* v0 c. Y+ `' T& K4 z! M" m/ M
- perror("connect");& h2 {4 Z. ]0 p7 ~8 e/ D1 A
- return -1; //exit //pthread_exit
! B2 h- y- N6 h7 [4 l# N: W1 q2 s, u - }
) y2 F u. ]5 R5 ` @$ r - printf("connect..............
( H$ @' t" U0 [8 U: M; j" s% v% o& [ - ");
" W7 z( u7 s# H! d: ]9 K7 V - char buf[100];
4 n" x) o- v; N - int ret;1 O# e E6 i& B, D
- while (1)
. z7 [$ k2 F- x( r3 s - {
, u9 Y4 [7 [- ?, T+ T1 X3 E - printf("send: ");
& ]4 e" M3 b# q5 g - fgets(buf, sizeof(buf), stdin);8 n- b! K5 V' C+ n1 U1 L* C4 a, T1 e
- ret = write(sockfd, buf, sizeof(buf));
1 h. S- \8 o# I5 E7 m0 T+ C - if (ret < 0)
- o: k' [ g2 b/ I5 O/ X4 |! } - {
$ ?1 }: \ K! v) d) V, a k4 B, e) B - perror("write");! r% x8 u6 n; L
- break;
7 Z7 L* D& j: z) |2 B; N2 D) o - }: h# H+ B8 _( { A x
- if (strncmp(buf, "quit", 4) == 0)/ o- D" s3 B: |, y9 _
- break;
1 w2 M7 w5 D: f; W4 n - }
* r! k, e' W; \( Y1 M! \6 _ o - close(sockfd);
9 V/ }% o/ [: W {3 L* q' p - return 0;
& r+ r: I3 I5 N# b2 S+ ^: F - }
复制代码
) } r/ J. F7 @5 J! z1 c! Y* _- y5 G2 @& z( C
|
|