管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
( Z& N0 \* l1 x$ I6 |
5 x) p: X+ a7 B& E: d( r2 I% f2 u, R* o
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
& B, u; X; j0 H# z/ r5 f3 }6 u7 K8 T& g& X" k6 _" c
7 P9 V5 S1 c; r# @+ B# I, J# jTCP协议9 k/ Y5 H6 N7 O3 R
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- x5 L3 j5 f$ Q/ b( x$ [+ z
% `0 ~; `7 G! t
/ F! T5 O+ J/ Q3 R关键词:三次握手,可靠,基于字节流。" c1 s1 m2 Y) o+ U0 T) p- K
V+ K8 C' F% t; b7 w$ k6 W+ y n
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 O% S* ^) h7 h; z2 U
! Z- D- ^1 f% rTCP服务器端和客户端的运行流程
/ K5 i4 {( j- L9 _
: S5 G# |; j0 S3 k) k2 ^9 t: e& X& c, N z# M) }% H V) i
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?' H% i; D" t2 n2 y4 J% P- I
2 t2 H! o' K' y* u5 k" [# m+ q- P% G
1.创建socket
. L, q- Q- G* U1 Z2 ~& f) A3 i socket是一个结构体,被创建在内核中+ D1 ]% k1 p. C2 C
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
: p$ X* Z& W6 W1 I6 z+ ^$ k$ i2 U& b5 r7 J2 ^8 q
/ V% i/ g, f: J7 m% ^/ U2.调用bind函数
. O* R; |! U$ t7 ~$ T 将socket和地址(包括ip、port)绑定。
1 _; X2 e3 Z" L* v 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序4 x2 Y! R# s! d- C
struct sockaddr_in myaddr; //地址结构体" O' S' f0 c/ S& S9 d
bind函数: W3 [( ~; F( O2 V; S. Q
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
( Q6 a) g4 K4 p% B) ~; S* z3 E( ]! W, c# ?! ~* s. {
# P9 s" @3 i$ v, o' J
3.listen监听,将接收到的客户端连接放入队列
3 ~6 Y% p7 z. `/ t7 @; @ listen(sockfd,8) //第二个参数是队列长度2 a/ H! p# [/ Z) j. Q, r9 p
! i7 i% b* C1 z4 h3 G6 [
0 O0 \8 m x1 F$ q+ ~! W) s' Y4.调用accept函数,从队列获取请求,返回socket描 述符) D1 d# w0 g. ^
如果无请求,将会阻塞,直到获得连接8 T( p h- p: K" A5 N5 }+ h
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
/ `) f% F5 H* X7 }4 c: P: u) v4 V7 [8 k5 G; d
2 Z5 p( r* J% ^8 y3 I& O
5.调用read/write进行双向通信
& o5 Y5 T0 E7 S1 _- N) o7 l; V1 x& r5 }4 i) O- C' o! i; k
, q3 b( ] @( N$ s. |, M# d6.关闭accept返回的socket
3 N- g6 _: r9 |; o close(scokfd);4 Q8 I1 x/ l$ }, N p
7 q. R9 E( l7 |& D! Z: s7 f3 r8 ]" R; E2 p
8 u* m1 y0 G; q( x3 t a7 z
, g! e% A$ ^6 z下面放出完整代码
+ ?- V+ d, x! j6 J) V
( Y! }5 h) u. O- /*服务器*/
! n4 T0 ]5 |. v! ~ - #include <stdio.h>
3 }$ h( F: q1 ~8 I5 r z - #include <string.h>: _; D2 _! ~- k5 S. y! _4 h
- #include <stdlib.h>
8 C2 k. x* R# n7 w M5 K9 @5 ] - #include <strings.h>5 c- a# d; g7 Z! `6 u
- #include <sys/types.h>
9 j2 f4 T2 X- T - #include <sys/socket.h>7 P! Z2 K0 h! v: n( [, x- q1 x
- #include <arpa/inet.h>. [1 a. O% V' S! `
- #include <netinet/in.h>
8 b# m) {: R" r - int main()2 Z9 L( p& b+ o2 n+ k
- {& Z6 P" v6 v+ l1 y# h
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
. b* j3 g3 V% C! o7 z - if (sockfd < 0), S! L" {+ M/ [' z; ~. s8 [
- {
4 y& e2 ^# Q' N; @5 Y; g& U - perror("socket");7 T$ s7 L1 D! q, @/ ?
- return -1;
7 ~' W, f6 U9 Q; S( f/ k& f k( ~ - } //创建失败的错误处理- j& \( ?4 N" @" H! j
- printf("socket..............
8 c: [% P I1 i, U# J3 R8 K - "); //成功则打印“socket。。。。”3 ]9 i+ }6 k* G8 p# h/ t
-
9 G6 i$ x2 o" m8 @ - struct sockaddr_in myaddr; //创建“我的地址”结构体
+ Q9 n7 z) ]8 R& G% e9 m - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)/ i' e2 R" R' E
- myaddr.sin_family = AF_INET; //选择IPV4地址类型+ q9 l9 v' a" B; t+ I' \ L
- myaddr.sin_port = htons(8888); //选择端口号
' q% m; X2 Y5 k7 h - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
- L. q- \3 Q5 x4 Q& k7 c - 9 \2 q" z3 y( c* s& y* E
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字9 ^/ B; H/ F$ w/ @+ P
- {
# W; {# l2 b4 i/ G/ t0 o - perror("bind");
3 W) Q% l- X) s - return -1;4 O1 v) O( q F2 K' n2 a
- }3 h/ E a1 [6 N- s8 ~( w# t
- printf("bind..........
: x# h; K( j2 ?5 A; |4 ?1 R - ");/ y2 t5 f/ p8 h$ N
-
' q, p0 z% |0 r9 {0 o/ I, b% p+ p - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
) S6 V$ Z3 r9 N0 A O - {9 k. q5 J' B8 v( A) B1 x. H2 O
- perror("listen");5 |8 ^( P. O z2 w% j
- return -1;7 f+ |- X1 [' i" {9 y) {6 d2 w8 m
- }
; e6 r1 ?2 i* }( E+ n1 |3 B3 W - printf("listen............
5 S+ s; _& C, y+ s+ Z - ");
" R1 @# A& t6 R' p7 N9 u -
) Q2 e- J; U5 H( J - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 U4 x# u0 B0 p; _# v& j
- if (connfd < 0)* ?7 F, D8 ~! W% N/ h' R
- {' G. Y* R* A. B
- perror("accept");
, l6 z3 n( n' W1 N" V) Z - return -1;' L3 E4 o' s% N* i+ ~/ e1 J4 Q
- }+ T k8 ?, F6 w% {
- printf("accept..............
# x4 Q8 |9 N+ q7 Y" j - ");4 n) |; m+ m; q: d5 M
- char buf[100];//定义一个数组用来存储接收到的数据
1 ]& q' b5 @; j8 f0 { - int ret;
; C b2 ^) i2 d1 [ - while (1)
6 n k+ W$ F; W2 K7 Z - {4 S1 G2 _' v! i) X
- memset(buf, 0, sizeof(buf));
4 @8 U+ Q/ h, G6 p% m! F0 Z - ret = read(connfd, buf, sizeof(buf));
. S* Z! T- E/ y* }9 Y - if (0 > ret)- C* {( _) y6 c- i9 N
- {
( \6 t8 v4 a6 i/ S - perror("read");
6 @7 Z: V' ~; K) O. x6 {' }* i5 g - break;: J9 v% |/ a7 I3 A! y2 I$ X: M
- }//执行while循环读取数据,当
1 b2 \; x3 a# U! L2 a - else if (0 == ret)+ y( A) t8 O6 \3 |
- {
! @ I- D* H: b/ N9 d$ A) ~7 [ - printf("write close!
, p. C8 G( X# B+ S& h - ");
" A' S: b* W8 o7 W - break;! i; \* @9 O7 B3 t
- }& `/ a/ @' B% {/ h6 ]: ~; `4 Q4 N
- printf("recv: ");9 L& U1 q+ ?7 T6 ^6 P7 m
- fputs(buf, stdout);//打印接收到的数据2 Q( U5 d! e. c7 s, e [
- }. p6 o; |, q7 h3 \: d' a- r
- close(sockfd);//关闭套接字4 l8 o4 G, A% f! O0 B/ g
- close(connfd);//断开连接
3 ?7 h! o( }' s7 u F& u( n - return 0;* c* z- o9 c) w1 l5 N
- }
复制代码
# ?5 i v9 X' k) n5 r9 H
& `5 s+ N l+ W4 |' l- /*客户端*/(具体功能和服务器一样,所以不再加注释)# \8 p1 c8 M( C. L6 G
- #include <stdio.h>
# O9 N6 J2 Z! q* ? - #include <string.h>2 c: v) l4 W& ~3 M. i9 r
- #include <stdlib.h>
/ ^# U& Q3 H# b/ o- ?* z0 J - #include <strings.h>8 V8 Y9 ^1 f: _& ]5 ]7 H& w
- #include <sys/types.h>
# m% M. [! O/ {* S. R. Q - #include <sys/socket.h>, q0 ]$ k8 S+ F+ e+ K
- #include <netinet/in.h>
* f) {0 K# g8 N* h - #include <arpa/inet.h>( W8 p' J) d" a" X: d
- int main(); y/ x9 B7 R: r! j% W
- {
+ m2 C1 j8 J: W; ~ - int sockfd;
l8 k; @& Q: O% r. W9 J - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
4 t# a9 n; Z5 t ]( C7 v - {
. E: U- f/ P( r, \) x2 I9 A# ]3 a - perror("socket");
/ v$ h- s8 E8 {& o: h1 j3 U6 O, f0 U - return -1;% i# t8 B$ ^& e* E! Q$ F
- }
# _+ P0 g' } x# q. h/ Z6 y - printf("socket...........) g9 B; j, p! M6 E( V- C
- ");- S! c2 S* J5 x0 G3 V, t- @
- 0 Q: I# B0 \. k- x& n& B
- struct sockaddr_in srv_addr;. @- O: `: j& T9 V" n
- memset(&srv_addr, 0, sizeof(srv_addr));
8 e: k% D0 l7 x' N% d+ _. V - srv_addr.sin_family = AF_INET;
! L5 z- T9 p- b: b - srv_addr.sin_port = htons(8888);
" e0 I7 G6 I% v p - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");. _( k7 a) O& M' n" w# S! g
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))7 V" j" n5 H. c) ?
- {9 ^- G k% W) E) H8 Y
- perror("connect");
# r! O! L5 ?- `. g$ [1 W; i! D - return -1; //exit //pthread_exit8 C. E* \' ^) \ i2 D
- }. J b. c( q$ q+ Y
- printf("connect..............
& C* P5 c$ L0 o1 |/ I - ");- x/ h. i$ \( f& s2 D! x
- char buf[100];
8 w4 I9 {* E- g: @) O) B - int ret;/ C, T) J+ ]7 P
- while (1)
4 u W/ E* A# }, ]9 _/ }2 H0 u - {8 F' B; `# w% w: G! a/ ]+ z
- printf("send: ");
8 q* L0 q; e' `/ Y% ]0 M# c - fgets(buf, sizeof(buf), stdin);% e: V$ \' D# j* Y$ j! E
- ret = write(sockfd, buf, sizeof(buf));
. b' z& {: Y/ |, z: g+ J% a - if (ret < 0)
6 H8 {4 O; j a- ]: z' V+ w: j - {
: m# d5 w( u/ R/ W/ e - perror("write");$ L4 b6 `8 r7 }5 N/ t( K
- break;0 j, w+ ~- \7 ? ^! c- |
- }
* i6 \- |) L, O5 P* q - if (strncmp(buf, "quit", 4) == 0)! o5 S3 X* m* m, K- ^
- break;
( Y) @. R8 j4 u8 ^ - }
( x# F: }4 J; [* e) D& u - close(sockfd);2 T9 M! j, ~/ I. o6 @
- return 0;' f0 O+ g7 h% Z" M
- }
复制代码
8 B1 \5 z j$ V" \- C1 }8 r
$ Z8 P& i# S( A! P( V) }5 z+ w( g) q |
|