管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。$ h8 A4 O: s3 F3 ^
# k1 D. a0 b9 X
1 U! F+ G' ^, o
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。" x- [1 {, J7 U# Q4 j
9 U& u$ `* Q3 z5 g" B2 @5 ]+ u4 t
+ B- q# H3 G) p! I0 E7 xTCP协议9 o. Y9 F3 u6 v
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 N- V/ ? K. t0 w, b; r- M0 K! w& a
/ X# n# y" V$ M1 F7 ~1 s \- B3 c关键词:三次握手,可靠,基于字节流。
. j3 F5 h2 M$ K, e' q# w- U$ u- X$ B
; o/ P( |$ y+ o% D& _可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" }6 l" Y* W# {0 w3 L C
8 k- J4 t3 S0 H& N9 m+ bTCP服务器端和客户端的运行流程
" `9 Y: x2 y# E! y/ B9 k3 c
+ Z: c: o7 m1 K& r. T: R+ A$ P( ^) Q4 m4 ]
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 M) W% F- s) N
$ Q+ u) G8 t6 n+ o! f1 F" m# n) \3 K3 f! [- h
1.创建socket
4 d6 R; U4 H2 i8 N7 j w socket是一个结构体,被创建在内核中
$ Y$ V( c+ |, L7 _0 s/ i y% T. |( M sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议' |" p- i7 j& M- ?4 m* e
- j: i. c7 v# y, Q
* |6 A# I* Q, {. Q: f4 L
2.调用bind函数
X) {+ {/ K0 |& U; Z 将socket和地址(包括ip、port)绑定。
. I. L( y1 i: B9 \ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序/ C0 ~5 Q, b1 s5 v" z
struct sockaddr_in myaddr; //地址结构体$ ]+ {( {; q. C! W) N) M2 v4 p
bind函数; P- N4 m0 z \2 P" @" B8 v
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 ^/ F6 b* a+ y4 I/ q# r
1 |! Y! M# y# @$ Y, h( p
+ g! D* g8 }' t3.listen监听,将接收到的客户端连接放入队列# s" o, X5 N$ [5 ^. r+ L
listen(sockfd,8) //第二个参数是队列长度
" {+ Q# L; N& v4 Q" N1 l: D" T5 C8 h+ ^9 ^9 |' l
( m% o7 J+ I* N4 z s: z6 r
4.调用accept函数,从队列获取请求,返回socket描 述符
; c! u7 e |* \# x 如果无请求,将会阻塞,直到获得连接- M) g3 e! E; H7 G
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 F! J3 o5 y; t( ]. o: c. f7 f% }. F. j/ Z
. n3 l) l; X J* y5.调用read/write进行双向通信
7 Y3 d, ]4 [7 L( r. h" Z- z, G6 [! H0 T3 p, S" l
8 X) i" F2 ~! s( T- d1 y
6.关闭accept返回的socket
9 M3 U q6 o; ] close(scokfd);2 ?* D" i( d4 `. i
& k: m4 Z/ k- S9 L6 W. Z
6 ?' P* X" ~7 P! A
* L5 q2 J8 g- D C( x8 R8 j5 R Y6 D' U9 s- ? I" z Q& T! M
下面放出完整代码+ c+ o% s5 m9 [, n# F* m
/ h; u; Q) q" {5 _: B- /*服务器*/! `- I: e3 E7 j L( f
- #include <stdio.h>
( ?% }! P; F+ E) L5 W+ v6 o/ Z4 Y - #include <string.h>* @& F& L* P1 o+ O% D
- #include <stdlib.h>
5 p5 o3 r* }' m# i7 z! o7 c" z' ^ - #include <strings.h>
2 h. O# e8 K" ~, R - #include <sys/types.h>
; u- d. D: L( g - #include <sys/socket.h>( C: p9 O! V, S5 b& s
- #include <arpa/inet.h>8 E0 B3 Y; E0 y5 F! d' S; y
- #include <netinet/in.h>
. V: M* l. V; A( Z+ d5 K - int main()
/ y7 {) Y- N- D6 c* n" U0 Z+ j - {
0 r. e7 s4 r' W* h5 T+ y5 [ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
# W! ^# k( e0 {: h. n4 m - if (sockfd < 0)
1 ^9 P, X8 ?9 I7 b9 o; L2 o - {8 I$ H9 \. F) g' K% M4 ^
- perror("socket");
# ^' d2 t" ]& E6 t9 v- _ - return -1;
6 X2 s% N+ H6 f Y. |8 n: z - } //创建失败的错误处理
A5 W3 O- w9 h7 w4 v$ q- ^ - printf("socket..............
. S/ R4 Q+ X7 Z6 J2 w, a: L; ` - "); //成功则打印“socket。。。。”( v4 c/ @. U: M; B7 D0 R! @+ `
-
- q. ]1 v. Q2 Z5 _ - struct sockaddr_in myaddr; //创建“我的地址”结构体
$ B$ p4 n1 a8 ]2 i3 a - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): y7 N/ R6 C; J5 o* L3 m% F( a
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
0 w2 O" @5 Q5 y/ [ - myaddr.sin_port = htons(8888); //选择端口号
' k1 W/ Q8 Z. ^0 f Q: [ H - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址4 y ^3 r3 a2 }: L; {/ F( T: ~! x
-
5 h; z+ J0 B [$ F' g; ]8 H2 a - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字1 h: B, J$ ^6 W9 [
- {/ J5 L7 S- L" H& S0 M% r, ~
- perror("bind");
; _ {6 W6 x& \ `" ] - return -1;
7 ?% l1 s. W5 Y+ F! N) ~" x - }) Q+ \6 u/ ^/ h3 v; O/ b
- printf("bind..........4 b$ f6 w* M# j% h
- ");* p! E% q7 b" }. D5 N* V1 L
-
! N0 U5 t( Q% f# h7 N$ ~) ? - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
- L, o& C5 l% Z9 _; q7 N! H; O' X - {$ K4 X" M, _& G" O. |
- perror("listen");
$ C: w+ p; \2 x9 ?# e - return -1;4 Y) ^, { j& l' k( d" w
- }
+ } t- k+ c- ] - printf("listen............$ r+ N( b! ~0 @
- ");# ^: |$ k) G% o9 g) r
-
% s- g* D5 v' E, Z7 Z$ S/ N& r - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
4 v- s& S9 T! w. r& p: v8 t2 U - if (connfd < 0)
: `! r8 g* W8 v. z7 _* X- ^% t - {" L9 n4 J5 x4 h/ p3 T6 F
- perror("accept");
4 w# @7 K# `0 F+ ~ - return -1;6 l" |3 h; M/ |3 t% h
- }
( k- s( W3 g. R. j - printf("accept..............
1 J+ o% S6 X: Z# G - ");
% ?! y9 \# r. ]* y - char buf[100];//定义一个数组用来存储接收到的数据2 } U8 n, j8 }! {/ i& J2 S$ C5 W
- int ret;/ x$ ?9 W4 T. r+ J& d# S
- while (1)2 H: _4 G% \8 V6 v( x
- {
3 Z' ^! ?0 y* f" D - memset(buf, 0, sizeof(buf));( @ ^: t2 w* t5 T3 \: X& T
- ret = read(connfd, buf, sizeof(buf));
' h" @# r2 @ v- e - if (0 > ret)1 w* ^1 `, n8 G7 d3 k! [! `( L
- {3 n8 k0 i) j' ~/ \* {5 X
- perror("read");
% L7 P# E1 N" X$ v - break;
, t7 t+ @/ O8 L6 \' O9 C' _. n - }//执行while循环读取数据,当
9 A5 D! v4 ]) j8 ?: \ - else if (0 == ret)
# S7 |* I+ N! x( `& J3 _7 f - {: M; J: e) @8 p% c
- printf("write close!2 s% N) R, t; _' g- j
- ");' \4 N3 t! ?, y7 [% Q
- break;* j+ O3 F% G; u7 a7 c, p
- }
# t+ y0 a9 d, I - printf("recv: ");! y; @8 n! @! s" V0 v7 N. \
- fputs(buf, stdout);//打印接收到的数据" n3 j5 D! K/ c3 M0 L
- }
- n' Y$ S8 W, W8 I) R - close(sockfd);//关闭套接字
0 r/ h" W6 {+ ~( T" D1 g p6 H# N - close(connfd);//断开连接
. I* h" Y) G3 q \ - return 0;
: n; g' U9 C5 q9 F - }
复制代码 * O! N+ M, e7 s y: H( h
* r) a* K( `* A2 q1 M0 ?9 L- /*客户端*/(具体功能和服务器一样,所以不再加注释)) m$ Q' T @5 ~
- #include <stdio.h>
9 A3 G9 k+ d4 h' _+ x* j r8 H - #include <string.h>
3 y" [! G: O. Y( S - #include <stdlib.h>
7 ?4 {: f$ k/ |" k$ z - #include <strings.h>4 {* Z+ Y) q J1 E7 \ S
- #include <sys/types.h>
" K" F( Y! U0 j* L1 U - #include <sys/socket.h>/ p7 r) y2 z6 t* m0 M0 [( h
- #include <netinet/in.h>3 W# [5 Q, g0 G+ w# X& T
- #include <arpa/inet.h> z- s4 F I4 I8 f7 W/ d1 A p
- int main()
; Z0 ` u2 o8 U/ { - {9 f/ V2 Z3 \9 G. t
- int sockfd;: D7 H* l5 L* |* K0 A
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! y1 q% T$ e! e. c2 N. X$ i* S% a( k
- {
7 e/ A! h P# D" } - perror("socket");
+ c5 }& L4 s9 N$ E, o6 { - return -1;: g4 e1 e! L8 [4 s+ r
- }7 }" j& k m8 ~" e5 y7 Z8 |
- printf("socket...........0 N, k2 a5 m1 k$ u7 m9 i* U! b+ H
- ");. _0 H3 E* r# Q+ R) s; [7 \
-
% s- s1 I& b7 g, q( N0 H, h - struct sockaddr_in srv_addr;7 k- c! g( G1 j2 c; c. j3 G0 Z1 N4 O- i
- memset(&srv_addr, 0, sizeof(srv_addr));% X/ f' g$ h- M" t
- srv_addr.sin_family = AF_INET;) C# S; v% A: m/ x
- srv_addr.sin_port = htons(8888);
9 K/ k" c: m# ~0 z; q+ E6 F - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
V* E+ l1 }# D$ W$ A+ P) u - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))8 U9 U! u, g" }3 o2 R
- {+ ~% [* z9 F6 m0 K2 s" y0 ?3 Y
- perror("connect");
+ o9 P5 V/ S/ J: G- p0 i6 q8 P+ d - return -1; //exit //pthread_exit
7 n# R# x1 I A6 {! P - }% a+ m% ?+ j# U
- printf("connect..............0 M" g7 h* [; q2 s [& B! c
- ");
; w8 }& H Y. O+ ]7 E8 J/ {. }; \ - char buf[100];
! @9 \7 j- I. r e+ N6 W - int ret;3 M* I* p: B T
- while (1), _' g8 }! r6 t/ L
- {3 \7 I& B! b- z. K# w+ A
- printf("send: ");
4 R( A5 A/ A6 m3 X2 ] - fgets(buf, sizeof(buf), stdin);" x, K. P; s% h! B, H
- ret = write(sockfd, buf, sizeof(buf));
5 C/ a) `; Z: ~0 w: H n* I - if (ret < 0)7 t2 i, s: K6 y- g! a+ [* @$ i
- {
/ b2 F1 L/ e& g6 u - perror("write");4 Z: C0 x- x4 d2 x. Z2 ?" H3 [- L
- break;( f' t/ m4 O' i6 E- h5 [
- }" w: c& @2 S, h8 A1 S
- if (strncmp(buf, "quit", 4) == 0): b1 |, A6 N, h9 Q# m! @
- break;1 P) Q6 [6 C7 U! R1 {8 u
- }# L- {! ~: B/ |0 q6 b( c
- close(sockfd);, ~& K2 a1 a: C3 k+ O3 ^
- return 0;
) O* \! ^ k0 ]8 l( n ~ - }
复制代码 ) Q- j, {: \" ~7 N, Q @
* i8 x% t+ Y# k& N1 j' H2 C) w5 ~ |
|