管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。! p, a+ y- p9 ]8 e+ a1 z
, }1 z1 }. w& w6 W
9 P( d5 g4 U: F5 X& d# N$ J. l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" W& p, g1 ], V6 {7 C* E! f3 |. a% }/ ~' f% Z
/ I d7 p8 c/ y+ _' A: x$ T
TCP协议
8 ^- N4 L/ A3 t w. y* \TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
' d! V! ^" v' [) @/ W ?
* e1 q" c+ W+ X/ F3 l: C& S+ I$ b- n5 U) ~. w+ S, O) T
关键词:三次握手,可靠,基于字节流。
& D# H0 s- C/ B- S0 a8 d( F1 V
+ f) f7 w! J5 {5 N. `3 I0 F" D
% J5 f$ y; f) T: r8 z- C' W可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ S1 T0 f" p" r1 g2 m% R! {
) u! {9 I0 `% t/ K* oTCP服务器端和客户端的运行流程
" Y' G- s' \+ P6 [
" l, A! d; }# X8 d6 q$ R5 ^, D( j( |# H4 k
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" @. E3 t9 |6 B4 b* n2 k5 I1 H& [
" ~7 Z. p& m7 Z+ s2 x1.创建socket6 T5 r5 f2 Q, t& [
socket是一个结构体,被创建在内核中* u4 `" a" N: z" o. A% E, d
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议0 a m; |- W5 B8 h, {' o1 ]7 u9 n
: r# _- Y2 [2 s) g L0 W
5 a# Y6 o5 D% e& u
2.调用bind函数
- C. Z3 d4 z* n V8 O9 E 将socket和地址(包括ip、port)绑定。
`. z5 i( W- U I& W 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 ]- W/ c1 t1 v9 ?
struct sockaddr_in myaddr; //地址结构体* M! y% W( N6 Q' K( g1 G; _2 k/ e
bind函数
h4 V% n5 V, a( W bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
) O1 d, `* M, l j; \* L& o0 H9 M
; m% Q; }/ p- C5 V9 F
3.listen监听,将接收到的客户端连接放入队列9 q! Q7 }6 Y! ?; R" F* w
listen(sockfd,8) //第二个参数是队列长度
& b( ^$ E# l( g) j! O. B
4 F3 ]" o% u+ j5 \9 s
' T9 _! q' t& [+ ]5 _# I: I4.调用accept函数,从队列获取请求,返回socket描 述符
- X1 t5 [7 a$ d/ G 如果无请求,将会阻塞,直到获得连接
: Z' R# n$ U3 U9 k# \0 e5 W% S, S9 Y int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
. F( a1 K) Y9 [& {# R
. e' G! e3 D* T, q* ?. k' P N! E! K2 ^8 J
5.调用read/write进行双向通信
" _' i) h8 `+ m2 e' n' v; U3 R$ q8 n2 X7 ^
6 o5 U7 p& u7 K# C
6.关闭accept返回的socket
4 M( l! v% q4 ^4 \, I4 Y" d k close(scokfd);! j! M+ e; D! Q) I
+ `9 K) G w0 k3 m! g5 t2 K
2 E' z% q! p) f# N7 w& S+ l; [: d8 w- T$ s' a( K
$ v" Z3 y9 o; L下面放出完整代码
5 j! H. B; g( O) w
" W: k# P. Q6 }/ {5 A1 Y- /*服务器*/
j2 @" q: A# ^! I/ u1 m! L - #include <stdio.h>
2 i5 L+ A1 R+ D* Z( ~ - #include <string.h> r* V. ]+ `# P8 C1 U
- #include <stdlib.h>
# t* z. ~$ T$ B5 k" U: c4 J8 x - #include <strings.h>+ Y: d6 T9 O! q' T% ]1 y
- #include <sys/types.h>' _% U; v2 x9 N6 u2 F% z8 G
- #include <sys/socket.h>
* a. D& P+ c9 ?- }1 a% Q - #include <arpa/inet.h>( {) H3 W0 o3 T$ q& l: ?
- #include <netinet/in.h>
) Q* Y8 w- ^( d7 |9 a - int main()
" _# K$ {+ K3 r! ~0 N0 ~2 ^ - {# Y! E3 t* K E9 u# j, g
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
" Z# Q# ]' z" Z/ I - if (sockfd < 0); Z' a S" r0 ~( G
- {
! ]3 y% }+ m/ Q! r - perror("socket");
& Z9 O+ L* P1 @) {4 U) X8 X - return -1;
6 v$ L6 P) P( d2 v+ o+ ~+ g2 @ - } //创建失败的错误处理
) ~% l& A% I# C' B1 | - printf("socket..............
% e8 p; ]* \) R9 o' i) w& R. P - "); //成功则打印“socket。。。。”
% {7 e# r: h% @3 X -
0 F, j" l3 B! w+ Z" o' K - struct sockaddr_in myaddr; //创建“我的地址”结构体# Q) M2 J& K1 V; V* i4 J
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
6 f+ e6 r; I) a& S - myaddr.sin_family = AF_INET; //选择IPV4地址类型
3 P5 f/ e" [' u8 c3 I6 _2 E& ] - myaddr.sin_port = htons(8888); //选择端口号
& L+ z& o' d1 Q6 g7 Z/ X1 P B - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
4 Q3 N3 A4 A+ w- Q6 c1 v5 i - , @9 |$ o9 O. M
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字# z; z$ k' r% p/ q$ w" S( M
- {. K9 U5 W: e0 r% o% C8 `
- perror("bind");$ | C# O& d6 ?9 |
- return -1;7 _1 r0 Y. j, u) o8 ~3 E
- }+ k( }# J7 b f! H) `
- printf("bind..........
9 b9 ?' s$ R* m J - ");6 g d9 e3 t- N- K+ m8 u9 O
- 5 M9 G2 Z( U4 Z. U, Z5 @. b
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
/ S# B% @3 z# t) | - {
J/ z+ C) i o- Y. j - perror("listen");% f% n( b2 ^ K L& H- P+ V' _
- return -1;
& b! E4 s4 H5 C6 T8 I' R" b - }& a* z) @) _# o$ d
- printf("listen............: p6 v1 Q0 I+ |0 G( p
- ");
2 x a9 U4 A' H+ K -
1 g7 O5 D, _8 ^ - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
4 k! A% w. I- {, ~. A/ L - if (connfd < 0)
3 g) P. \( B& z - {
4 m( t) W: I( ]+ Z7 l+ J7 |" O4 [ - perror("accept");" @0 n4 T6 M) s% K8 u2 O
- return -1;) d) ?3 [: R( b' f
- }
6 H J! ~. m" v5 Q5 ~) i9 ` - printf("accept..............
5 P2 b; V0 F: d2 \* s. l2 p - ");
/ g1 n& t1 E. ~) ]' M2 a3 l" O - char buf[100];//定义一个数组用来存储接收到的数据
" `; x; E6 }; e7 J - int ret;5 `* q" F- n9 Y
- while (1): T. Y+ K2 p; S/ Y$ r- ?
- {$ s) j9 J/ N$ m; c- |
- memset(buf, 0, sizeof(buf));
7 F+ M% b9 d! J - ret = read(connfd, buf, sizeof(buf));
7 ^8 Q. t8 Q, R3 H F" L! H - if (0 > ret)1 Z/ z3 t' C1 i: W& A7 u
- {* {" D( K% _: C% e+ Q2 Q0 B! u
- perror("read");
- O/ @% t/ V2 O - break;+ t+ I8 U4 ]4 I3 y7 S/ H; v
- }//执行while循环读取数据,当: ?: i) h: C6 f9 w" K F
- else if (0 == ret), \7 x. s* ~: `+ {" }7 O4 _* B
- {( w2 P2 L0 ~+ \: m0 e
- printf("write close!8 A8 b* \% `2 u" z, t# `
- ");
; R0 {2 C0 {; Y; y7 K - break;" F5 w L* ?5 M! ?
- }
' W3 {" [/ L! q( _' w - printf("recv: ");
7 f+ `- I' O! w: i( U; H - fputs(buf, stdout);//打印接收到的数据
v/ X4 k4 v# @% T; S; ~ - }/ x1 R2 z! ?4 t1 ?, }
- close(sockfd);//关闭套接字, b- i4 v- H7 p: s
- close(connfd);//断开连接
* H. g) \& ^+ H0 J5 z - return 0;9 N: @3 I! s2 Y$ s
- }
复制代码 + \ R9 i4 J: v6 k( y& z5 P; f
) K% x* Y. p2 z W8 x- /*客户端*/(具体功能和服务器一样,所以不再加注释)8 k% O: W& W1 J6 C
- #include <stdio.h> c, \2 k, q6 i
- #include <string.h>
# s4 X% q9 g$ m+ ^/ V8 F8 T) k3 c - #include <stdlib.h>3 x/ i+ t2 n9 S8 T/ b! ?3 J; X3 q
- #include <strings.h>
/ ]1 q: A) H6 ^2 A% w! U - #include <sys/types.h>4 T6 k% U- k* N7 d
- #include <sys/socket.h>' C1 {0 Q% z4 \: M
- #include <netinet/in.h>
) E1 a0 u8 B% H4 k. k+ w+ k - #include <arpa/inet.h>
) _2 s/ F& f) [. K0 c! f - int main()3 ~0 l# y x* e0 a9 \& r
- {
- N; J" {5 G$ c0 z - int sockfd;$ ]) K- W+ g' u/ c; `0 t. @5 p
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
! o/ B( t# |' r# {2 \ B; Z4 B - {1 L8 `2 P+ j5 B9 U; I$ @
- perror("socket");; v- y+ N) e3 I& ] a% e0 @
- return -1;
9 J8 t. H j# ~- g - }
0 E; e" g8 J8 r5 y9 a - printf("socket...........
9 ]( `+ v+ {% G+ q - ");9 s; q2 m! I& i' ], u
- : R4 a; ?* {+ V7 b1 q* s7 {
- struct sockaddr_in srv_addr;
* B K) Z+ y" w" O# t+ P7 {6 H - memset(&srv_addr, 0, sizeof(srv_addr));6 K5 I3 P3 {. e+ C: w& O
- srv_addr.sin_family = AF_INET;
c* v2 a$ f9 _# `& F _" k# r - srv_addr.sin_port = htons(8888);! m/ k, v0 R- z4 M* p
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");; c. D9 U; o. }2 g; g' B2 ~. u
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
7 i5 I' c1 k, S; y" x - {# e0 p q6 x1 } h0 m! K' [: s
- perror("connect");+ |- B* o5 [% \
- return -1; //exit //pthread_exit& `" _2 a" @- n
- }
& f$ P% ]2 s/ m, f$ D1 c w - printf("connect..............
6 e& ?1 h1 N: z3 n& t, { - ");9 } k7 n* O4 t
- char buf[100];( A; r4 m7 K7 v
- int ret;
7 i; ~# @- S2 z% e - while (1)! i) ?5 V. n4 ~1 b+ {/ K
- {) e/ w4 {8 A5 v) U7 P
- printf("send: ");
m) F) y Z- a% }$ j6 R& F) y; i* F - fgets(buf, sizeof(buf), stdin);
/ n( v$ h; f1 l+ U' O - ret = write(sockfd, buf, sizeof(buf));
2 e8 ^$ K" b8 g ^& t" b% a, E - if (ret < 0)3 ~* V, ~) h0 ?. R; v+ p, X" Z& X
- {! w: s& J0 t, ^9 o- E
- perror("write");, V, w- e" R5 r6 a) c$ B, c
- break;, U4 D7 Q& b8 c% y2 h; }
- }
( c( V% X/ f; ]; Q R - if (strncmp(buf, "quit", 4) == 0)
7 h( d9 U2 o7 u% K* I - break;
1 q3 p8 I! J0 r2 ?& l1 m - }3 |- l+ a9 ~7 K6 o5 R
- close(sockfd);+ |' e; D; L& o; |0 L
- return 0;* J, J- E r; \
- }
复制代码 % W# p$ D% d" i- Y/ Y1 j
5 W% C( Q& F% M: r9 I4 U |
|