cncml手绘网
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
[打印本页]
作者:
admin
时间:
2020-5-9 02:09
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
& [* j. X e7 d7 L# W) D V' O- Q
% [; O: Q' [9 o8 Q" f- q: p) [9 V) H3 \
B: Z4 t; ]( _8 E; @4 r
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
. i5 W# a' f/ r- s0 O$ @0 h
$ l% y1 ~* i3 i* H3 S
% A4 I3 c. Q. L5 t3 n$ V
TCP协议
* e' L1 ?$ M( n" l, u# u$ y, M
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 J+ U& Y: q- h5 ^! j( ~
; L/ L, V6 Z) e; H" c
+ K' T$ r9 H# a* X+ t( V! c( |
关键词:三次握手,可靠,基于字节流。
/ b6 W: u6 R8 l) t5 D; B: u
' M3 T5 a" y( N W& y% L; [9 j+ X
5 w+ w1 g3 y0 V1 T
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ B; ], f/ i/ {- J' }
微信截图_20200509015654.png
(175.4 KB, 下载次数: 6812)
下载附件
保存到相册
2020-5-9 01:57 上传
' S- h: ?0 T5 F; w) `: g9 D# S
TCP服务器端和客户端的运行流程
) S% v$ v( M' k# c ^! h j
: \) G( Y/ F d4 X- i: k
8 a0 f8 v* o. P; N5 e
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 S+ [7 S! S/ {! l6 k$ ^* X+ b3 F
: |' S/ p; l: }( {9 ~2 g( F
$ j& V3 |* n( |$ N2 o& j8 j7 c9 V
1.创建socket
, M% T; Q4 G( h
socket是一个结构体,被创建在内核中
# n$ M2 A. r. P4 X0 j
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
% `: T/ Q: P9 W' G
& j' r+ I6 D3 R) F( b0 @
* ?2 z+ J" }3 B9 u! f7 |, g+ ~
2.调用bind函数
7 U5 E5 n2 h0 P @
将socket和地址(包括ip、port)绑定。
. x0 j- A- D3 ?6 ]+ U3 H
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
8 r6 I6 w! c, s) R6 h( ^0 |8 s
struct sockaddr_in myaddr; //地址结构体
5 b1 |5 L$ P/ h& l
bind函数
" g* l3 a7 K# ?, `. s3 Y3 {2 M$ g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
' y2 u# l+ R7 @# {( ]
5 T, d5 V* ~' R6 H
- M8 ~! {( ]2 q2 j6 b7 e' ~1 H
3.listen监听,将接收到的客户端连接放入队列
: s7 G. F& l& N t( c1 R% i9 o
listen(sockfd,8) //第二个参数是队列长度
0 W$ q- K9 y1 T1 w
7 J) o$ @! T5 m* [- k) A& f
* ]/ e& ]. `7 ~$ ]* l6 I
4.调用accept函数,从队列获取请求,返回socket描 述符
e% t$ t. e" o6 [
如果无请求,将会阻塞,直到获得连接
" s9 L7 L) |% a
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
- N: \- [% Y0 r
) s8 G: T f/ _6 ?2 F @1 ?
4 U+ L2 C0 p* O( B$ O O
5.调用read/write进行双向通信
) ^0 U+ z4 R8 |
9 x8 e* f. l/ D6 [; U+ T# ]2 ~
% S, t6 W, p" L1 i) ~. n- B3 ^8 N5 U
6.关闭accept返回的socket
L( D s7 q* W: s, \- U
close(scokfd);
, }, j; O& {4 W7 B* Z
2 m' C4 f; Z9 p( [/ a* [
M: G% b* B. ]
3 v4 b$ d" c! o0 |* [! V: h% E
. k. a6 ]1 P5 e9 O$ \0 ^
下面放出完整代码
9 }/ j0 _8 D8 Y6 R7 B1 H9 l* N# g
0 D7 ]# L- b @+ B- f( [* S
/*服务器*/
& q& D0 F8 s* X
#include <stdio.h>
' X: x: _1 f- F2 c
#include <string.h>
0 @4 e" |1 B6 [" w5 G7 B0 _
#include <stdlib.h>
, w" Y! E9 x4 v8 `9 Z
#include <strings.h>
& N9 r3 k" V( [5 D$ J
#include <sys/types.h>
- s, s2 Y4 J, }7 U1 o
#include <sys/socket.h>
" V; I- s, U/ R/ ^) W
#include <arpa/inet.h>
$ w. Z/ m" U3 [. s% ^
#include <netinet/in.h>
, n8 w0 @5 _) h: r) {. q
int main()
1 C, j- d- y5 a" k
{
' A8 `4 e* P4 U5 D
int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
. |! S9 r3 v: X- `
if (sockfd < 0)
9 m; s* W1 P2 s* r
{
/ i! _& a1 E: Z
perror("socket");
% n k5 t: y; o. o
return -1;
7 p: e( Y) B/ _4 t# B. o4 Z
} //创建失败的错误处理
2 \3 V$ z. P1 F1 Q7 P7 U: B, J9 g
printf("socket..............
! r! h5 z# j- W2 w# j
"); //成功则打印“socket。。。。”
. V9 [& D2 _4 T" N
! v3 `$ N, |$ E, Z- \. a
struct sockaddr_in myaddr; //创建“我的地址”结构体
! |% X- L& N, Z! P! M ]
memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
+ Q- A1 H, T/ E# B2 R6 h( M* ^/ o
myaddr.sin_family = AF_INET; //选择IPV4地址类型
7 \/ ]" s" A6 y" F+ K9 Y
myaddr.sin_port = htons(8888); //选择端口号
+ a! @: M0 ~( j! k! \* R/ p0 d
myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
1 @# G+ ]2 Q% j x' Y( g/ k+ C# ]
3 R0 G4 U. X+ ^6 x& T, P
if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
3 E/ i3 Z P+ Z$ M' E
{
' P. i2 K5 ?2 u1 a( g$ Y) P
perror("bind");
$ {+ K$ Z0 W% ]5 e! ~
return -1;
6 k B3 G T& D$ X0 C% R
}
+ R0 B! i/ Z3 F8 y
printf("bind..........
) K! l* M, l( p" B4 c1 m. p
");
: Z' c( L$ v% P3 V
; y" D* W" a0 ]
if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
/ w; L1 [1 r9 X
{
{/ T% d, B6 J
perror("listen");
# q, w/ k- Q7 `4 {! S' P
return -1;
. V! `+ b$ V5 ?! X& X0 h
}
! w) t0 Q8 t; U. ~" n
printf("listen............
k3 a0 D. k8 m2 T+ d
");
0 \8 _ U, O* H! ~/ ]% \* d" q0 q
8 e: k/ z' V* _7 G; X$ l; u8 z
int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
0 x; Q- r' n5 Q' e( D, B
if (connfd < 0)
6 k: v7 l- i0 ^7 n+ P
{
- Y5 H @' n" }% A5 g9 P& g
perror("accept");
s: a! ]& U. A L) i0 ^& }
return -1;
' y/ y6 y4 x" v: ?9 t, E! C
}
" ]% r: X: f9 \# W$ u2 O
printf("accept..............
3 L/ r7 F9 b8 }% F
");
1 T' s0 H/ {, ~$ }, Z
char buf[100];//定义一个数组用来存储接收到的数据
. D4 k5 p& [0 K. e0 h5 Z
int ret;
4 s& L0 ]( A6 H: E4 g$ {. J; j, A0 \. x
while (1)
! L. h8 n$ Z5 R" @2 P
{
0 G/ W1 \& ~+ h: i7 j' d5 l& p
memset(buf, 0, sizeof(buf));
- R8 r8 g6 @" I: [$ k c* L4 m
ret = read(connfd, buf, sizeof(buf));
& b: a" z- `/ [7 |1 t
if (0 > ret)
; D- z' q9 j9 x# z- G
{
& c, p2 {, R/ O+ T, q
perror("read");
: O0 [# Y [: ^1 Y- f* D2 ^
break;
" t( P& ~+ Y$ j5 H3 |7 J; _
}//执行while循环读取数据,当
9 i8 a7 z$ Z+ z4 }* x6 t- I
else if (0 == ret)
7 z# W6 r: R; J
{
8 t4 }0 J+ h" ]( _8 o
printf("write close!
6 Q6 i* l) Q* t* E4 W3 o, z
");
/ Y3 w# c- T. }- D9 P
break;
; s* s8 O7 [/ w R" b9 Z
}
2 V3 p* a" J* _4 O
printf("recv: ");
# M! q* ], [! c2 |, B9 Z
fputs(buf, stdout);//打印接收到的数据
" l, n7 z7 B6 \% }0 {. v/ R" X2 o
}
) j: B; r' v+ @/ A4 Y5 b' j/ |
close(sockfd);//关闭套接字
3 ]3 r, v, M" R
close(connfd);//断开连接
! `* L3 a; k) _! B
return 0;
4 P+ Y2 i% C$ o N& h
}
复制代码
- ~! H# D. U( M( o- h& G3 H
% K" X d3 H) d8 w8 {
/*客户端*/(具体功能和服务器一样,所以不再加注释)
, v+ I7 J% ]+ c9 j. e0 g+ V# E
#include <stdio.h>
0 T" t5 y' D6 I! D6 ?( A$ X d
#include <string.h>
0 ~9 { H2 j, ~4 n" \% M* N$ _
#include <stdlib.h>
, m! |$ ~7 ?+ u7 u/ X' P- a+ w
#include <strings.h>
/ m) b5 y! X2 K; ] B
#include <sys/types.h>
& }/ r6 B1 T8 }
#include <sys/socket.h>
1 F/ A% S4 B; y
#include <netinet/in.h>
$ f9 D& h$ s9 t! _' c
#include <arpa/inet.h>
% \& z1 p' \2 R) i7 l+ J# m# [0 l
int main()
5 _8 Y) U" c! i; R, i5 H2 P$ ^! x
{
' k" E. j1 O) j, }" H
int sockfd;
! V6 n" B0 R0 u) v, X% Y5 p
if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
) n. s5 k1 i9 Y! \! e0 Z6 r' g
{
1 z0 o. {0 J8 e
perror("socket");
" ], s V M- C$ H" V1 b
return -1;
1 Q; L% x3 A$ G3 Z. a7 |
}
4 H) L/ {6 w5 v4 O8 W, d
printf("socket...........
$ l5 [9 t; H/ K& H5 `: r/ Q# \# \
");
9 P$ K( m9 k& H5 u; K
$ u8 K- U5 F5 T# y5 B
struct sockaddr_in srv_addr;
$ b3 F7 x; A* l; R$ v/ \
memset(&srv_addr, 0, sizeof(srv_addr));
2 d/ I# M" J, p: _
srv_addr.sin_family = AF_INET;
9 g7 P) F" T. O, k$ a6 S
srv_addr.sin_port = htons(8888);
* S) C' ^: i! {7 B( V3 ^0 ]5 v; N
srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
' y1 Z" G. i6 G, x" N0 H' J/ [$ J
if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
, u2 n* T7 q; _9 h7 z3 I" ?+ h, K: |
{
% F( I" d U4 r
perror("connect");
: a5 z: B4 e" O/ B: d' t m" k
return -1; //exit //pthread_exit
4 D4 t8 N+ s1 {/ u! H9 h& u
}
5 E* s$ _: v! T
printf("connect..............
8 X- f3 g2 \; Q i! t
");
9 x7 p& o: j; j8 K; {* q: c9 U8 G
char buf[100];
* M0 {/ K! P# u3 i; ^7 Y
int ret;
' ?) c& H. b' O0 ?5 }5 r
while (1)
1 o: @$ Q+ g/ O! P+ r
{
* H' m5 \( m' b7 V: b0 e5 u6 P
printf("send: ");
# s" _, F) m, p/ T& `
fgets(buf, sizeof(buf), stdin);
* n) _7 B' C3 w7 Q
ret = write(sockfd, buf, sizeof(buf));
5 M7 X) X- L# V' N5 A4 E8 R! ^
if (ret < 0)
9 `; b& R+ O# q9 O
{
" p6 ` M/ [' j+ Y
perror("write");
& h, M4 ?: i p8 \: F
break;
, X2 G. j" H1 n- p
}
; s4 y* L4 d5 b
if (strncmp(buf, "quit", 4) == 0)
* e# v& _8 W2 {7 g: i9 s1 H
break;
1 n+ V1 Y4 K; i7 W- |7 L- d4 i
}
- Y' d3 l' o/ u
close(sockfd);
( {1 k3 Z+ [# V4 L3 ?) k% |' f [
return 0;
6 H7 _# N( {) Y) [- q
}
复制代码
- w. ^+ T% ?& W& N8 Q
+ R$ ~& N0 h( v: U, y- t9 w
欢迎光临 cncml手绘网 (http://cncml.com/)
Powered by Discuz! X3.2