管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。) X# o2 x; @0 B# o- Q
3 i6 @0 s# H) ^# i( Y$ v1 l) Z0 g+ ]; S; H7 M2 n" }- _1 ^
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
& c( g, L! w O2 B, `; Q a* |; U1 A A% F: v. O/ V6 j8 k5 p! O7 H* x
3 n4 d; P( J+ s6 x! s* mTCP协议7 T8 g7 T" y( c4 Q& Y
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。 A8 ^6 I4 B, j
4 A+ e/ Y$ c$ J a1 m2 s* U2 o1 _7 ]) c" s' S0 \8 K6 ?2 Y; h
关键词:三次握手,可靠,基于字节流。- O+ `: r! o) c3 d3 y" L8 P
: V+ ~" X% {9 d8 w! M! B% N. h; ]2 P& y5 |
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
: q( J% w5 r+ R
8 n! P5 H$ c' ?$ ^* i4 i8 S. J4 `TCP服务器端和客户端的运行流程
7 l( s- z' \0 e, n7 n6 g+ \% @
! j' T' k, G, b$ D0 o3 l
/ w+ k2 h: ` {/ u# e& q如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?, g" n1 @( ]: m& X' T- Z
6 r" G# S& Q) e/ h0 }8 c( J
- V. O/ W. F7 z. Z4 v0 W$ y4 n1.创建socket
) y1 G0 k$ Y- K9 i socket是一个结构体,被创建在内核中8 _# B& f p% u; G" ~8 P1 [+ T
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! i7 _: F5 Q% ~, _8 S1 T3 t4 E4 q5 |1 ]4 e
) I. U) S4 Z2 N" b/ V
2.调用bind函数$ a! m0 ~6 p; {, X" t& @
将socket和地址(包括ip、port)绑定。
# U; @9 z6 P- q7 \/ F1 n 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
' {/ _/ g8 d8 K2 ?: } struct sockaddr_in myaddr; //地址结构体
$ _0 W" W7 M" J5 W; V bind函数# C5 j% P4 c4 Q5 a% `8 r" `
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)); g" n8 r& Q( l( U7 b
4 C/ X: y; u8 P( S( a. n6 {4 Z
3.listen监听,将接收到的客户端连接放入队列
3 n4 w6 x5 L6 T listen(sockfd,8) //第二个参数是队列长度, |- E# V4 s$ v6 |. W2 T9 \+ t
( ^& |# X/ A- \0 v. o' E) b# J" A' v q7 k9 M, {. ^0 u9 {# N
4.调用accept函数,从队列获取请求,返回socket描 述符
' A3 n* [" q }3 X: i 如果无请求,将会阻塞,直到获得连接2 G' A/ b9 f* v8 U, {
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 `% S) c3 o% V$ h5 W5 q+ d5 h$ @! x+ e
. }% D0 M" }7 L, d" A+ O* f5.调用read/write进行双向通信7 q! P: ]7 e! U% P8 ?
: |& e/ V5 n; y/ \ s
( s* z5 r' P4 S! R/ H
6.关闭accept返回的socket* K8 H0 f' {' d O+ j5 k3 U
close(scokfd);
/ @4 w0 Q r" |4 g
1 _( ?! y* D8 b U
) q+ B% k e4 g, x
' a) F5 d8 ?9 e# H# r" U3 z% _# a6 J4 ?$ f
下面放出完整代码
' F. [$ _0 J- ^$ R1 P) o& i5 S( v) E z* [
- /*服务器*/
1 {$ m* ?/ i0 z - #include <stdio.h>* p b, f! D9 e
- #include <string.h>5 v ~; z2 {) _* P7 l( O
- #include <stdlib.h>& O }5 K/ {4 f
- #include <strings.h>. }! }. m4 F! E4 V
- #include <sys/types.h>* A# l6 X: o# K( @, X5 U+ t7 q S
- #include <sys/socket.h>' k+ e1 y- K4 d0 a4 h' R" F1 F
- #include <arpa/inet.h>" d: `5 n, ^& ]
- #include <netinet/in.h>
- {( r* n) K- ^( k& G - int main(). C2 {; Z8 J9 H% u
- {
+ H7 f( ~+ a' T8 \ b0 A - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字( q7 c. e+ |. b" h/ x6 Q
- if (sockfd < 0)7 {( w9 B; y, @; _# T- C, L
- {9 K5 _0 N$ Z r0 J8 |
- perror("socket");- `+ C' `* E: U0 R& n
- return -1;) @. D) {! G9 |- z' {
- } //创建失败的错误处理
5 B/ w% O$ I% y7 S - printf("socket..............3 K" |* @6 b( L6 P
- "); //成功则打印“socket。。。。”
: u6 G" s/ ~% h5 I4 ^ -
& Y6 b" V6 B1 ? - struct sockaddr_in myaddr; //创建“我的地址”结构体
& P& X: E# p; O6 _ - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
0 ~' ]: c- x( f G' k' X - myaddr.sin_family = AF_INET; //选择IPV4地址类型
; ?8 g. |5 U0 ?1 T2 U9 f. b) S B - myaddr.sin_port = htons(8888); //选择端口号6 g4 {& j3 [4 N: Q7 w
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址9 R5 w3 p) o4 [+ s
- 2 F- K2 c3 ~; P+ E& q0 Z9 N' Q
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
! Q. j8 p; i& }5 O - {' `4 x! z5 t( f6 H2 L! n
- perror("bind");
9 L) {1 J8 N( K* D; h* }: s - return -1;
5 V* m# ]7 B/ y4 b5 p - }
9 |% }0 R$ e, r+ ^% k- R- E - printf("bind..........
2 a* S$ @1 r, Y - ");
/ Q* H) ~) o( M5 ^% t! ~' K8 | -
! B- W6 u$ l, V0 U - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 D# V4 V* p/ q* D7 Q
- {
; g$ K3 i. `8 S3 @$ E9 o - perror("listen");8 n0 J/ @& }! J$ \/ _! o
- return -1;
9 F/ m# p* f, v7 Y# @9 b" ? - }
8 A/ w) Y7 }, c. D - printf("listen............. ]5 t( ?3 g% \4 z( j/ Q" }# {
- ");
6 f* L( ]" P2 {4 o: q% D -
+ r6 |# |1 U& l0 n9 Q- ?0 ?7 b2 ? - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
6 y" A& S/ w+ f) _ - if (connfd < 0)
8 g/ \, I2 l! k7 w- {3 Z! x5 v3 ` - {
' Y2 {* Q7 G) X, m - perror("accept");9 [+ [8 f9 R3 i s4 T, M5 d4 H
- return -1;
; O) @7 w% G# { - }/ z4 a, i W' V9 C; k& |3 [4 t
- printf("accept..............
4 r3 z j& l9 M" C+ Z4 p1 c - ");& m5 ^% W) {: T% K3 ^* v$ X
- char buf[100];//定义一个数组用来存储接收到的数据
- d( U# ` }# c3 T; c( o' ? - int ret;8 U6 L& v1 I/ p! u8 B& f
- while (1)
) E+ P K/ z \2 ~4 [1 i - {1 @$ o( ?; H( ?6 ~4 w6 Y
- memset(buf, 0, sizeof(buf));
, l) X4 N" m: ]6 B( w - ret = read(connfd, buf, sizeof(buf));
# V8 M, y) N, x/ p9 m" ~: q4 Q - if (0 > ret)
" D5 a5 v6 [# a C/ C0 e: T, n - {
, @9 v% w6 W9 h+ c& v& | - perror("read");
2 r# f# `! p4 b b! }$ K- ^* v+ K - break;$ v' y! k) V9 W0 }0 d
- }//执行while循环读取数据,当
. E4 M6 _- x1 ]( [ - else if (0 == ret): _4 \" b9 t% h' Q( L: b+ p& l; [
- {
% B. ^1 F: ]' b, I - printf("write close!( B; ~: P! m, b. w5 S
- ");, W" d( T5 r2 g- j
- break;) p4 y9 J9 U, B8 n
- }7 @0 t) ~$ ]+ B' v% n
- printf("recv: ");
2 r. E" I5 B$ O& o# ^! i. B - fputs(buf, stdout);//打印接收到的数据
9 o5 k; f% H. s7 ?! f8 ^ - } n9 o6 R9 E7 k+ t, ~
- close(sockfd);//关闭套接字) T Y& A: l6 S! E& j
- close(connfd);//断开连接
/ M& W/ w& ?0 e3 I' [% x- p - return 0;
7 y& ?2 e4 t5 E; p, v+ C - }
复制代码
7 Z, h4 o4 {# e# K& i) T* G5 a0 z( Z; j$ Y3 a) C: Q. ]
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
/ _; p. c( J8 {2 n' P" L - #include <stdio.h>% r) P4 [! Q. Y4 O' b; v! Z
- #include <string.h>
9 ?2 s8 e" }, Z# M8 \$ Q - #include <stdlib.h>4 i. q/ {2 s [' ?3 `) b
- #include <strings.h>: }/ Y% g- R; |# T0 R4 ^, r2 D4 P+ e
- #include <sys/types.h>
& w/ i1 Q+ t$ \* P$ H/ ~ - #include <sys/socket.h>: f2 Y; X) C; d0 N# R! O w! j
- #include <netinet/in.h>; F9 ^# j* r) e @ n7 r9 i" O
- #include <arpa/inet.h>% ~# ]+ ]/ ]3 L- s# M
- int main()$ x8 g% i' ?. x/ ]
- {8 Y" l6 \' ^& L
- int sockfd;
' E& K0 E) O: y" S- S. v0 j1 A% i - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))2 U+ d5 Q5 j4 ?: f I9 q W
- {9 d# _8 N( I4 J3 [2 |
- perror("socket");0 D" H8 s, s; o8 D$ g
- return -1;
/ \# g! X4 }" }% \ - }) ]* S' T/ I6 i9 n( [5 M3 M, c, w
- printf("socket...........* R( |; W% P9 h8 R; H) V, W' X
- ");
2 p2 d5 P+ o) B' y1 P6 n -
1 u- H2 H: R, s. E6 F: d - struct sockaddr_in srv_addr;: o+ l+ i @) u" K& ]
- memset(&srv_addr, 0, sizeof(srv_addr));
& ?& u# S2 Z$ l) r# E - srv_addr.sin_family = AF_INET;1 R' D2 m7 c) N i2 U8 W
- srv_addr.sin_port = htons(8888);5 H/ u. a8 F8 e- R0 `
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");+ B& w0 g l) D3 ~2 E
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))8 W) E2 s4 r6 R+ L7 S( B
- {6 S( [! v) H8 X" H
- perror("connect");
( `* }/ j, _, X7 O! ~! J& s+ s4 d! D - return -1; //exit //pthread_exit. H8 F! }9 H/ t3 i; M
- }. a! S; p* O+ P" i, y5 z
- printf("connect..............+ e- g8 m+ n, ~8 ~5 {
- ");
7 t+ K8 E, n2 i% a0 C# @0 F ?- g - char buf[100];
1 ?. l- }' o3 ? - int ret;- Z. o3 n) @0 A0 h- D- N
- while (1)
, l; N) Z( z- T/ b' \0 p - {
8 ^. Q+ v. K+ h$ X - printf("send: ");
1 a# V" H V+ g6 w% D- [. w2 s - fgets(buf, sizeof(buf), stdin); d& l8 M) l5 M- g* }/ Q" L) Z# _2 c
- ret = write(sockfd, buf, sizeof(buf));, d% V j/ d9 I+ e+ q: r3 x( i
- if (ret < 0)
$ b9 F* x2 S" s* o1 {+ J - {; @& X5 x! g" a' W9 s
- perror("write");
9 N' c) N5 k! V, k- @3 K4 u: F - break;9 I1 f8 l7 x; k; u; I5 u: s
- }
+ k% }: b2 V# i2 T - if (strncmp(buf, "quit", 4) == 0)8 i8 y: b+ ~. i s6 S' z
- break;$ y3 V" t" _4 h. J- K1 h; `8 P$ ^
- }
0 E% @3 H$ k6 M/ t$ @# ~2 {. o - close(sockfd);1 r3 D* n5 y" k+ Z2 J
- return 0;
5 X" K2 ?4 H$ Q$ r2 H+ i x - }
复制代码
& P1 F0 l' \* [8 q* O2 C
* U, {9 T# N7 C+ r; k |
|