管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
, }- B( s# L9 ?$ r$ l& U ~% T7 a7 C, ?$ ]8 m
. [2 C7 O& {9 q* jsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ W$ [. {* z2 L4 b3 h2 W7 r% K
3 i' U" F# s* Y/ |" _& H" K$ i+ b* F' y d9 k
TCP协议
) e$ I# `, X! GTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 O! y& p5 \" Y; k1 D4 N" [) @+ [2 k: w/ ]
, Y- r( z8 g* D7 u1 \
关键词:三次握手,可靠,基于字节流。4 R8 c9 l7 U# k! F0 L# C1 `
" q# `5 K/ |" M5 B) w
7 `9 [) f% |2 |+ i$ g2 I! F可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
2 F8 T# ~3 i7 p: U* S5 J
: S4 V2 a1 w+ i4 r+ K6 R7 l! Q
TCP服务器端和客户端的运行流程" d/ J" Y0 v$ {0 D$ V# Z4 j9 x
8 `- c7 S# ], F# o' J8 H+ x ~3 K
- i: ~" P- G0 O6 d0 F3 D- R如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
& F9 t7 ^0 m5 ]& [* p* t* i" w6 H' K. O" F. I; @5 [* W' V
P1 O3 [' j3 J9 {/ d' ^
1.创建socket9 U! n( g5 j9 U
socket是一个结构体,被创建在内核中
- o' P# O; D4 a! }8 u4 v( \ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 |% b; T8 }- F# B t
" ^" ~- U6 [1 y) N& U2 r5 a% h( ?# j3 g0 N9 E7 T3 y* q
2.调用bind函数5 v# y* s2 Y! I5 l
将socket和地址(包括ip、port)绑定。
4 j' X! a( v$ _ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) @3 I- u+ @9 W( W
struct sockaddr_in myaddr; //地址结构体& k8 K& M5 s/ y. M* y; b
bind函数# }; j" d/ j! h& l/ v2 } Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 q. q3 ~( p! y0 R2 ?8 X! S. n; \# g7 d1 Q8 y
8 j. q+ j& {# \! J2 |3.listen监听,将接收到的客户端连接放入队列; M; b/ b1 B2 L9 s" @: `
listen(sockfd,8) //第二个参数是队列长度2 R5 l: Z+ W% ^
# t M7 \! {7 X7 A. n
& ?, s# V' \7 w! n Z& \/ D1 P& E4.调用accept函数,从队列获取请求,返回socket描 述符: j- V( w& V8 F8 \% o2 y: Y
如果无请求,将会阻塞,直到获得连接
8 F8 K+ B/ _" J l int fd=accept(sockfd, NULL,NULL);//这边采用默认参数' I k. {' O$ v# z' u# |! F; `
# c8 g4 n: M3 U4 T1 S+ s# W" y/ q- x6 U6 G/ p6 z0 M( S; \; h) J
5.调用read/write进行双向通信0 N& C h* h: ^* @' Z8 C& g
6 w1 b) F3 d7 j: n% O% x6 V' Z" S: O, ?4 s+ P* q7 f
6.关闭accept返回的socket
2 Q& x: ]4 v$ x- K close(scokfd);6 T6 s! P) X" o9 N+ T& d
& m7 @" {- T( K5 L6 j% R0 ]5 G i* X
9 ?5 H! `3 n7 h$ p, E, a3 C
' F5 _" i8 X/ k下面放出完整代码# X2 x9 F# e" c/ q% r; J* x
0 i7 v: O; k. f7 U% s
- /*服务器*/
( ~+ s9 D2 B0 z7 n, S* E - #include <stdio.h>8 i( z8 I" F; l& U; W; g& i! _
- #include <string.h>% {/ ^1 s# r' W
- #include <stdlib.h>+ c( B5 [" q1 S: k
- #include <strings.h>
; J3 u% Y8 a5 `' J+ H) K4 w- @ - #include <sys/types.h>
: l! T; \+ H+ k6 }8 }! K - #include <sys/socket.h>
5 v+ j! @# U \# n* V - #include <arpa/inet.h>
$ S; e3 P! i9 k - #include <netinet/in.h>
6 }4 J; v; |" T2 ?8 ~ - int main()
$ _8 G! ~8 u/ h) O$ ^2 S1 X - {. T0 U9 O; u3 l, |% b
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
4 ~3 t, ?: K7 L/ G - if (sockfd < 0)3 a' ]' k' U4 T6 \
- {
# o8 z! u7 r2 N8 h g$ G& R - perror("socket");
" A- }+ U1 j& d4 e* Y - return -1;% @+ d( n7 d( n8 c' D! D
- } //创建失败的错误处理
! x8 H9 k U, C" c9 ] - printf("socket..............
4 i: a, b1 L8 x( w - "); //成功则打印“socket。。。。”
8 B. b- V2 I# d) S2 Y Q# g8 Z -
3 w' O( o; p9 b - struct sockaddr_in myaddr; //创建“我的地址”结构体
S2 q4 y+ H# d$ { - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
0 `0 X8 z# O: r! b C - myaddr.sin_family = AF_INET; //选择IPV4地址类型2 r, {2 s/ T7 j- Q; Z% k( o
- myaddr.sin_port = htons(8888); //选择端口号
6 U1 E; X) Q7 T) z c. [2 Z/ { - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
. \8 w1 s U, r -
+ E8 w* S1 E' E9 Q" D u/ d - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" o0 I8 Z1 A' |" d# Q
- {2 `5 o) t& ~2 J; \- ~) O& S; ?2 o
- perror("bind");( m5 f" j- }% e, ?$ d& R7 s
- return -1;
% V% j/ [( {* r9 z" ?( { - }
, T% R/ Q" }9 g3 a& {" ~5 Y* ^ - printf("bind........../ a: w% B' m4 L% G. z7 L$ _. t
- ");
+ o+ R" `& Z1 t- w! B - ) L5 a+ B1 z" _% s) }# _
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
: U+ J$ ?: S- A6 g6 g - {
( ?% ^& R$ L% F% {) Z/ r: O - perror("listen");8 l1 z9 F% |" @1 x8 f5 t) _
- return -1;* ^* I/ Q& t( J k
- }# ?- y+ q" L& x* Z: c
- printf("listen............
7 Q5 N. M* P/ n( L' l" D* D1 M3 s - ");
. ^7 M, T8 g; l1 e9 S - 5 k- z4 r& Y9 S0 b" o
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
6 ?" a9 j# }" k8 U - if (connfd < 0)6 S, r9 a6 r' x
- {7 P3 R- L6 l/ q, \6 D; w
- perror("accept");
# g1 h8 o' z& ?5 U+ m# X. s - return -1;
! x3 T5 f& W! u3 c' h' d: @8 }& |, l - }
1 k, M8 A1 w' v- X - printf("accept..............
- h+ Z4 N! g/ b - ");
: |* |% m3 @- @8 a! Z4 D5 V - char buf[100];//定义一个数组用来存储接收到的数据
' H0 w9 v; m! O - int ret;8 [6 |, ^0 m, V
- while (1)5 l7 v% c: H- \7 K# \: t
- {
+ e) |% _+ w. o! Z! N - memset(buf, 0, sizeof(buf));8 Q% I, h5 \& k1 _9 u5 x. y
- ret = read(connfd, buf, sizeof(buf));
: R" j: U( `2 W9 L. f - if (0 > ret)* @) V% F |+ I" H
- {% R) U" f* z; g% e% |2 s) p
- perror("read");; m& M2 T* {, w1 k, y* r |
- break;/ d' |/ B5 n+ a, A3 V, g# t: b
- }//执行while循环读取数据,当, ^% b! X) j) R0 ~
- else if (0 == ret)
2 a/ ^7 e. J& v3 l) P0 @ - {. P9 ~7 `5 @$ m2 M- E9 ^
- printf("write close!
& K% ^2 L/ Y* W$ v - ");9 `2 `7 r; l1 d$ }; o
- break;1 |- ]- }; {+ s/ ]4 L/ q
- }* S9 O5 M! v8 B5 _+ i9 u
- printf("recv: ");
w1 o0 C9 @# G. ?( Y8 `! K8 s3 r - fputs(buf, stdout);//打印接收到的数据; Y8 P, _+ h' ~1 [
- }
. A, A8 [5 h. N3 Z* R' C% [ - close(sockfd);//关闭套接字
6 n! t+ @) H$ j - close(connfd);//断开连接' ~; R* ~' _1 d B1 Z4 j
- return 0;
* H. K, P6 J0 g9 u* C" G - }
复制代码 2 T+ W5 W+ [. I3 \' ^' _4 K
; j3 A: z- y; h' T- y6 p* |" K& Z- /*客户端*/(具体功能和服务器一样,所以不再加注释)4 h9 f4 N- f3 B8 z
- #include <stdio.h>
! y$ B- X8 e5 N( i - #include <string.h>! x- f+ G7 S [; U% p
- #include <stdlib.h>* @# `3 \" @, ^1 p) i
- #include <strings.h>8 s. n4 J9 K* d3 G6 \/ g/ [6 R
- #include <sys/types.h>
1 j* c( J7 G" r - #include <sys/socket.h>
) j* _( H8 j, K! B7 O0 ]- Q - #include <netinet/in.h>$ q p" Y7 w7 {8 |( y: G/ l# f4 j
- #include <arpa/inet.h>
; q/ [) p* L! o' \+ u2 t0 x - int main()4 d0 G' a3 J/ @, ?% U' n
- {: C: q4 `/ s4 a' m
- int sockfd;0 q" ?$ b2 O8 p9 I# E
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))). O& p9 R$ d* E: m' }
- {& k0 L: a; Q& J4 M! }4 h
- perror("socket");
* t3 d) n" B4 n. z4 x6 @ - return -1;+ H4 g# h, Z: K( Q: G# K
- }- r* {9 g* L7 C4 U
- printf("socket...........5 z+ w% B. ~0 g/ J
- ");: p; G& K$ d/ ~9 }0 ~
- 5 I' `, R v4 \& M6 i" |
- struct sockaddr_in srv_addr;+ e* T+ Y* p# U5 g% t
- memset(&srv_addr, 0, sizeof(srv_addr));
+ h- Q3 B! z6 {: q+ ? - srv_addr.sin_family = AF_INET;
& u/ f* a" W. S' l - srv_addr.sin_port = htons(8888);. {( h5 | G5 y) k. a0 F; X
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
) N4 e# u) g5 L3 n# n# Z- ?. N - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
$ ]1 C* D9 T: z/ }7 A" k - {2 T# z7 k* C, Y; _5 D
- perror("connect"); A: \0 }( h& w; G( I
- return -1; //exit //pthread_exit* R$ M4 H6 K9 a/ h9 f) E' |4 @
- }
0 x5 x/ Z5 n& f+ x: u( ~4 Y - printf("connect..............
# Z1 R8 U& j* v% L" T- o6 t- @ - ");9 t, X- u/ \" L9 ]! r
- char buf[100];. U1 ^. W( B7 X/ `& m( Q ]) E
- int ret;; D9 x* W3 M6 T/ T q
- while (1)
" r4 E- y& l* Q3 ?& r' P, N# t# } - {$ c# H7 H- n# F$ \
- printf("send: ");
; q- ~+ }& }. x7 x9 f/ x+ H - fgets(buf, sizeof(buf), stdin);5 b1 A+ Z/ y- z6 Q2 T7 n
- ret = write(sockfd, buf, sizeof(buf)); o8 @; O, w+ m; [
- if (ret < 0)
# N0 \& ?1 [4 C - {; g* H, [, w$ l Y4 `( e
- perror("write");
# l8 L0 y! ?* H - break;; q4 F- A! l! _+ J/ P
- }& @) s& z/ s" G8 i1 S6 W/ u5 y
- if (strncmp(buf, "quit", 4) == 0)" D; l6 e* `: a6 e% B
- break;2 U6 Q) L5 Z3 O5 \/ _
- }
- V" }% \; b4 b$ @" |* }( F* ~* L - close(sockfd);% N# l b$ Q: P" K
- return 0;
# l% T- @7 f+ o- h% e - }
复制代码
9 n4 T* X, N3 `2 y1 {1 a& B/ {; I) h( X3 W: _# t/ @. W4 a. T6 [
|
|