管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) B3 {! I6 J3 ^/ j, `2 R8 M1 M
! S6 u q! g& f" Y0 Z% _" Z0 R: F
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。" `: h; m: f, F; _7 I
# f$ U8 S- g" e( L9 X( b, c ?- S
TCP协议* J) G( X, m5 b) ^# L0 C# H# S. d
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。* S/ d2 p( @6 x3 |( l ?3 M' J
7 z. M- \1 X, M; J( s; A5 Y6 V! i- D% j
关键词:三次握手,可靠,基于字节流。" u$ S; t8 t* z4 t" Z7 t
. n5 l1 `" z7 b. j4 ^% |+ l
/ X h c, V" Y! |可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。( L, Y" Z) B9 J
! P0 t6 B' y0 D) {" W
TCP服务器端和客户端的运行流程
% X" _0 n# G6 N# E' c
! a1 N4 ` g2 u j
. _3 n% ]3 ?9 w( Q3 ^' `如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 O0 @3 H: i% D& J
$ i0 u& t- U$ D
& O& S& m5 j& {8 S9 l' n1.创建socket
* m8 O$ t3 b2 i5 U6 J$ _ socket是一个结构体,被创建在内核中
0 u8 |) c! A- Z8 v sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
9 x* X3 c" V3 G$ P- T. Q) t, o; X k0 z0 P" j* z( l5 n
# I+ n& j9 H* `& ?/ U2.调用bind函数& e* ~( h' G! P- _1 l( i
将socket和地址(包括ip、port)绑定。* S( b& N4 C' W4 r
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
9 ]: L) S; T9 r4 e# Z1 i/ z, ~ struct sockaddr_in myaddr; //地址结构体
2 Q$ L* \ p1 Q, Z) l1 v9 C bind函数
2 _5 f. | H% A bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* P3 J& ?! v+ @3 [, [6 }- ^" g7 h$ O# H z2 H2 c8 C
. |# X! [" t' P- ]% Q/ T( I1 r3.listen监听,将接收到的客户端连接放入队列
; W0 ]7 T5 f+ a* ^" C# U; E; q listen(sockfd,8) //第二个参数是队列长度
: z. ^$ R9 D( g0 \2 J5 j
# u6 A# O w! Z6 R4 E
f' @# A# b+ _/ H3 d3 y4.调用accept函数,从队列获取请求,返回socket描 述符/ n' j* e. D8 G5 T4 L, S' M
如果无请求,将会阻塞,直到获得连接
1 y' t- v9 D- p5 I int fd=accept(sockfd, NULL,NULL);//这边采用默认参数: W% |0 D- E$ e( ^! e7 k' {
9 Q1 D# `6 }1 E2 ` q6 |
5 q3 Y! Y* l# i5.调用read/write进行双向通信
2 i- A I: ~2 G. C
1 J9 P. m7 ^ ~3 Q- W5 k% j8 R: w1 z2 T7 m0 a
6.关闭accept返回的socket8 P; w+ o2 G* g4 j; R$ W% G3 A
close(scokfd);
+ F# J5 e* M" I8 E: ?4 i: ]2 y. P/ X/ O, ^0 ^7 `6 e) E
3 x2 |0 J9 J3 S* r0 H2 G0 X0 a+ |8 @$ H0 o- k0 e( e& n
& L2 ^! t9 l' |3 B
下面放出完整代码
" _! y% c4 ? q9 V. b; e( Z( A# D. A+ s) j- ]1 v6 a0 h
- /*服务器*/5 J& w* C' S" J- B0 ?5 p+ l
- #include <stdio.h>$ W9 n, d$ i$ ?/ O0 [
- #include <string.h>3 n& ~6 T' F. N. _, F- I6 Q" J3 l
- #include <stdlib.h>6 l$ ^ @! ?$ |
- #include <strings.h>
7 |" Q8 K! X" x( @ - #include <sys/types.h>
! [, U6 i3 R) G% o, ~8 ~; d5 n - #include <sys/socket.h>4 m$ A- Z' Q8 Q* K; I
- #include <arpa/inet.h>$ V5 w5 J/ a, K& m
- #include <netinet/in.h>
/ C+ f: c) x& V6 w+ {" V; w& { - int main()
: m3 ~, Y2 A% M! E - {
# ~' }( R, ^' H+ F - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字9 |" p& f1 G/ r }' L' n1 Y
- if (sockfd < 0)8 l9 g, h3 D6 y2 `! f; {) r
- {2 o5 J; O5 j8 |7 ^1 y
- perror("socket");2 K7 v1 N c1 t& T
- return -1;
, @( ^% ?" L, _! X O4 s( \; ?$ q2 a - } //创建失败的错误处理
( ~4 I* W" w0 o6 P }7 Y - printf("socket..............0 `7 I h+ K2 D
- "); //成功则打印“socket。。。。”
0 [/ R4 y4 K* L# z5 f, H# E3 H* g -
& r; b0 E- d- r7 Q - struct sockaddr_in myaddr; //创建“我的地址”结构体
4 S9 Y+ y$ Y& k# k8 f5 C5 C7 P - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
, R% V, P0 N/ x8 f+ R$ ] - myaddr.sin_family = AF_INET; //选择IPV4地址类型
1 `7 G$ {+ S* f U' m6 O1 ^2 a - myaddr.sin_port = htons(8888); //选择端口号
' t1 N9 T) b e( @$ J - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
5 V: O+ m* z" X -
\2 M0 F% z" L - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
0 H' R* q) F# P0 T - {2 n, Z' b% l9 y* w3 h: l" e) N6 `
- perror("bind");
* w3 A6 k" u! { - return -1;
) ^9 g8 C+ {- m7 r+ M - }
4 w# x+ }, K( c6 d4 R; q. l8 b8 F - printf("bind..........
0 C( u! G+ N5 |4 g! }2 T - ");9 \5 |% g6 x7 a' ?6 o
- 4 i; T- x. c' D0 f% q
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 t% k. e# B. ~, J+ Y7 s
- {9 x3 F! O4 n8 z1 D9 X" u% [0 A7 j
- perror("listen");. d5 T$ x% @- {* J* A) N- |
- return -1;- Y2 U; r/ @; J5 _5 n- g
- }
2 |! u( ]9 C) _1 U l, ~% ^ - printf("listen............5 A/ T7 N! p* u
- ");" ]! O5 P: \7 d
-
# L, ^7 ^6 n! d0 \6 H - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求 F8 N* S% x% e% O
- if (connfd < 0): k% l$ r- b7 ~) B, l
- {
8 M( z* r) ~7 _' ^& }& G6 |3 A: e9 H - perror("accept");
+ \# y: c7 @1 f2 L/ r2 d - return -1;" s! }/ N9 r" p, P! F
- }$ P+ \& A/ |5 ~0 |
- printf("accept............... x; c v. `) D" {) q
- ");5 P ^& z A7 N* ^
- char buf[100];//定义一个数组用来存储接收到的数据. n4 ]0 `4 r2 o" O/ [1 c$ i# S
- int ret;$ @8 d* F9 t3 P s( {
- while (1)
( _8 A2 ?* M. s - {6 s0 S" i, A5 h7 z* a9 k$ `
- memset(buf, 0, sizeof(buf));
- D, Z& p9 A- J2 U - ret = read(connfd, buf, sizeof(buf));. ^( n, d& M' q# j
- if (0 > ret)
i: c( N- I2 k4 k2 @9 F - {9 X6 R, n( J8 r4 y8 e7 H, W
- perror("read");
" K! u( K8 S$ L! N: @' j* O - break;( {5 B4 D8 Z+ U$ A
- }//执行while循环读取数据,当
4 v3 S6 ?) X0 A0 Y8 k4 f - else if (0 == ret)# D" a; F1 L/ i4 d# ^
- {0 m' h$ k2 e# U2 [, O3 j
- printf("write close!
' V' C; V$ M" i+ D5 B. @7 v" b - ");( d+ ~+ n" K) i$ N5 J
- break;; u) V" s" h7 I* I# j) o: `
- }
6 v9 S, Q* o5 i! p* h - printf("recv: ");2 u$ u- S$ N5 ]& v
- fputs(buf, stdout);//打印接收到的数据! ]& g: n; ^; V% ]
- }
0 x) d) N4 ~" @9 e% Y4 h4 R7 x# R6 M - close(sockfd);//关闭套接字. y. s6 |( E* E9 X! b
- close(connfd);//断开连接
! x1 p8 A" `; Y8 @( E - return 0;
% @ F y, O- k" p( a1 }; d - }
复制代码
, C' [4 r" e: v1 a
' J# a8 Q% h, F: s$ t3 F- /*客户端*/(具体功能和服务器一样,所以不再加注释)
+ c- w5 s% W1 [) r - #include <stdio.h> @! `5 {+ F7 N
- #include <string.h>
8 l% E; o9 B, X0 r: O' X - #include <stdlib.h>9 `5 }" Y4 g b
- #include <strings.h>" t1 W6 _! b5 q5 t
- #include <sys/types.h>
1 y# u. j) { t9 q, T, K - #include <sys/socket.h>, @; K: g2 Z3 d& j7 j( E1 a
- #include <netinet/in.h>, S& O4 E1 h2 c; |! A
- #include <arpa/inet.h>/ @- R. n" Z" p
- int main()
' L; l6 V) l. l1 B( A - {% N) S2 f9 q8 _8 _1 e
- int sockfd;3 A/ z& q" Y' |$ D* h0 n7 \" R4 l
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))0 P( X6 h4 T9 T+ z
- {
) s; N- H' w: s7 I - perror("socket");
. u. y4 m7 A. F. N - return -1;+ {) D- y- U& A7 e) [) [2 {
- }
/ b6 c5 x' i$ C- n! X - printf("socket...........
' J) v* ~3 Q. f" U+ b ] - ");
4 v: M. q$ a. a" Q9 j -
: ]! d7 i) b/ B3 }) v8 y - struct sockaddr_in srv_addr;
7 q2 t+ H' Y! c& }- H, @ - memset(&srv_addr, 0, sizeof(srv_addr));+ Z3 [5 |/ F; m) B5 K# w% g
- srv_addr.sin_family = AF_INET;( |3 F' r# M4 V+ s, A0 I' A0 x
- srv_addr.sin_port = htons(8888);
; j) |+ Z4 w: s- u6 L8 k8 t4 C! K - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");! @8 U' q) j0 Z, A! Q# ^- F/ S1 h
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
; P8 ]1 F5 E" ` - {/ X& v2 y7 {4 [+ ~: X7 p5 Z
- perror("connect");
6 r; d) j/ ~3 p" _2 x - return -1; //exit //pthread_exit2 B- V* [: L9 d1 [! i$ O
- }
: z8 C8 G$ X8 z, E9 } - printf("connect..............& w! u- }" u/ I5 {% K, j( f
- ");# @# o& F; I* }: J T" ]( j; e
- char buf[100];
: T4 \- a1 X2 h% R- V* h- D7 j. t - int ret;+ O( s7 [ G. J) n! ]% V8 B$ b
- while (1)3 V' a+ x9 C# ~( ]. e
- {& B/ p, B' [) a3 K4 x" V: O
- printf("send: ");# S" q2 W) r4 n& p
- fgets(buf, sizeof(buf), stdin);
1 Z* g: A/ u' ^) x* R1 ?: Z - ret = write(sockfd, buf, sizeof(buf));
" p S0 l' _# s6 N" e! l' l8 [( l4 i - if (ret < 0)
4 m+ u: @2 Z+ U+ Z( n - {
4 M! ^3 Q2 k; I) u+ d1 P - perror("write");
. z0 u: c4 f5 p - break;" c2 S8 G( a8 E
- }
; c# B8 k8 E1 q - if (strncmp(buf, "quit", 4) == 0)5 P; r8 f/ a$ \
- break;
! p6 a# s% v' S! }0 l: r( ~ - }
% F2 w5 ~5 L* q Y - close(sockfd);: n% D. c! C- t: K: P* d5 ?
- return 0;
8 P2 G8 B' G" R, P% H; [ - }
复制代码 3 K3 ~. u- C1 l, P, M: u4 A( }
7 z4 f8 {; H# G# o
|
|