【UNIX网络编程(四)】TCP套接字编程具体分析
引言:
套接字编程事实上跟进程间通信有一定的相似性,可能也正由于此。stevens这位大神才会将套接字编程与进程间的通信都归为“网络编程”,并分别写成了两本书《UNP1》《UNP2》。
TCP套接字编程是套接字编程中很重要的一种,细致分析,事实上它的原理并不复杂。
如今就以一个样例来具体分析TCP套接字编程。
一、演示样例要求:
本节中试着编写一个完毕的TCP客户/server程序演示样例。并对它进行深入的探讨。该演示样例会用到绝大多数的基本函数。未用到但比較重要的函数会在后面的补充上。
演示样例要完毕的内容是实现一个客户/server程序:
1、客户从标准输入读入一行文本。并写给server。
2、server从网络输入读入这行文本,并回射给客户。
3、客户从网络输入读入这行回射文本,并显示在标准输出上。
用图表示给该客户/server演示样例就是:
大致分析该客户/server程序:首先。客户程序通过fgets函数从标准输入得到文本,然后客户程序调用write函数发给server。server通过read函数读取客户程序发过来的文本,然后server调用write函数将“从client读取的文本”又一次发给client。client调用read函数读取server又一次发给client的文本,之后客户程序调用fputs函数将读取的文本又一次发送到标准输出。
从上面的分析能够看出,TCP客户程序与TCPserver程序是两个独立的程序,它们能够组合使用从而形成一个“TCP回射客户/server程序”。
以下我们首先分别写好client程序、server端程序,并用Socket调试工具,进行调试。调试结束后。组合这两个程序,形成一个客户/server程序。
二、演示样例分析:
在写客户/server程序之前,我们先用图来展示一下各个阶段是怎样工作的。以及客户、server是怎样建立连接并传递数据的。
从上到下依照顺序分析client应用程序以及server段应用程序例如以下:
1、server端调用socket()函数创建一个套接字,由socket()函数创建的套接字都是主动套接字,也就是说,即使我们的初衷是在server端创建的套接字。但此时内核并不知道我们的意图,此时的套接字不过一个要调用connect()函数发起连接的客户套接字。那么该怎样使它变成一个被动套接字呢?也就是说怎样告诉内核应该接受指向该套接字的连接请求呢?必需要经过后面的bind()函数和listen()函数。
2、调用完socket()函数后。TCP此时的状态是CLOSED状态,接下来我们调用bind()函数。该函数完毕对ip地址和port号的绑定。注意bind函数通常是由server调用的。client一般不会调用该函数绑定地址,由于对于port号,内核会自己主动的帮助client确定它的port号为一个暂时port号。对于IP地址。内核将依据所用外出网络接口来选择源IP地址。
3、server端此时调用listen()函数,它的任务就是将上面步骤1中socket()函数创建的主动套接字转换为一个被动套接字,指示内核应该接受指定该套接字的连接请求,同一时候规定了内核应该为对应套接字排队的最大连接数。listen()函数调用完毕之后,TCP状态由CLOSED状态转换到LISTEN状态。
4、server端调用accept()函数。它用于从已完毕连接队列对头返回下一个已完毕连接。假设已完毕连接队列为空,那么进程休眠。注意。accept()函数的第二三个參数分别用来返回已连接的对端进程(客户)的协议地址和套接字地址结构的长度。
假设成功。它的返回值是由内核自己主动生成的一个全兴描写叙述符,代表与所返回客户的TCP连接。
accept()函数的第一个參数就是socket函数创建的套接字,我们称之为监听套接字。返回值为已连接套接字描写叙述符。
5、由于accept()函数会使server进程堵塞。等待client的连接。
client用socket()函数创建一个套接字后。接着客户进程调用connect()函数建立与TCPserver的连接(注意。client没用bind、listen函数)。对于TCP套接字。connect函数会激发TCP的三路握手过程。即,client发SYN给server。server接到后回发SYN和ACK给client。client接受到server的SYN后。回发ACK给server端。三路握手完毕后。连接建立。此时的client与server端都处于ESTABLISHED状态。连接建立后,我们就能够通过read和write函数在客户和server端进行文本的传送了。
6、当文本发送完成后,客户给server端发送一个FIN结束标志。server端接到FIN后给client回发一个FIN结束标志,然后客户接到server的FIN,在给server发送一个确认标志ACK。
之后。连接断开。两端的TCP状态都处于CLOSED状态。
三、演示样例代码及解说
首先给出client的程序并用TCP/UDP Socket调试工具进行调试,并给出结果:
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h> #define MAXLINE 2048 int
main(int argc, char **argv)
{
int sockfd;
char sendline[MAXLINE], recvline[MAXLINE];
struct sockaddr_in cliaddr; sockfd = socket(AF_INET, SOCK_STREAM, 0);/*创建套接字*/
if(sockfd < 0){
printf("socket function fail.\n");
return -1;
} bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(9877);
inet_pton(AF_INET, argv[1], &cliaddr.sin_addr);/*对套接字地址结构进行操作*/ connect(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));/*建立连接*/ while(fgets(sendline, MAXLINE, stdin) != NULL){
write(sockfd, sendline, strlen(sendline)); /*利用套接字进行数据发送*/
}
}
编译该程序:gcc client.c -o client
用调试工具进行调试,用该工具建立TCPserver。port号设为9877,然后运行上面生成的可运行程序./client 59.73.166.231(该IP地址。是我电脑的IP地址。Linux的IP地址是59.73.166.32。它会出如今调试工具的client地址部分)。
并在client发送数据。abc。然后就会在调试工具里显示该文本abc,接着发送def,相同会在调试工具里出现def文本。效果例如以下图所看到的:
由上面能够看出client的port号是53633。它是由内核自己主动分配的。
以下给出server端的程序例如以下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h> char c[2048]; int
main(int argc, char **argv)
{
int n;
int sockfd, coundfd;
struct sockaddr_in serv, client;
socklen_t socklen; /*creat sockfd using the socket function*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1){
printf("socket function fail.\n");
return -1;
} /*bind the ip addr and port using the bind function*/
bzero(&serv, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = htonl(INADDR_ANY);
serv.sin_port = 9877;
if(bind(sockfd, (struct sockaddr *)&serv, sizeof(struct sockaddr_in))){
printf("bind function fail.\n");
return -1;
} /*listen function*/
if(listen(sockfd, 5)){
printf("listen function fail.\n");
return -1;
} /*accept function*/
socklen = sizeof(struct sockaddr_in);
printf("accept wait \n");
coundfd = accept(sockfd, (struct sockaddr *)&client, &socklen);
printf("accept wait \n"); while(1){
if((n = read(coundfd, c, 2048) > 0)){
write(coundfd, c, n);
}
} }
跟上面的客户端程序一样。也能够进行相同的server端程序的測试,此处略去了。
四、完毕的TCP回射客户/server程序
因为上面的程序是进行的測试。client并没有回射部分,以下的代码给出回射部分。
客户程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h> #define MAXLINE 2048
#define SERV_PORT 9877 void str_cli(FILE *, int); int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in cliaddr; if(argc != 2){
printf("usage:tcpcli <IPaddress>");
return -1;
} sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd == -1){
printf("socket fail.\n");
return -1;
} bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(SERV_PORT);
if(!(inet_pton(AF_INET, argv[1], &cliaddr.sin_addr))){
printf("inet pton fail.\n");
return -1;
} if(connect(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr))){
printf("connect fail.\n");
return -1;
} str_cli(stdin, sockfd); exit(0); } void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE]; while(fgets(sendline, MAXLINE, fp) != NULL){
write(sockfd, sendline, strlen(sendline)); if(read(sockfd, recvline, MAXLINE) == 0)
printf("str_cli:server terminated prematurely"); fputs(recvline, stdout);
}
}
服务器程序:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> #define SERV_PORT 9876
#define MAXLINE 2048 void
str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE]; again:
while((n = read(sockfd, buf, MAXLINE)) > 0)
write(sockfd, buf, n); if(n < 0 && errno == EINTR)
goto again;
else if(n < 0)
printf("str_echo : read error");
} int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr; listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listenfd == -1){
printf("socket fail.\n");
return -1;
} bzero(&servaddr, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))){
printf("bind fail.\n");
return -1;
}
if(listen(listenfd, 5)){
printf("listen fail.\n");
return -1;
} printf("listen stat.\n"); for(;;){
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
if(connfd == -1){
printf("accept fail.\n");
return -1;
}
printf("accept stat.\n");
if((childpid = fork()) == 0){
close(listenfd);
printf("servers.\n");
str_echo(connfd);
exit(0);
}
close(connfd);
} return 0;
}
【UNIX网络编程(四)】TCP套接字编程具体分析的更多相关文章
- <网络编程>基本TCP套接字编程
tcp提供了可靠传输,当tcp向另一端发送数据的时候,要求对端返回一个确认.如果没有接收到确认,tcp就重传数据并且等待更长时间,数次重传失败后,tcp才放弃. 建立一个tcp连接会发生如下事情: 服 ...
- UNIX网络编程——基本TCP套接字编程
一.基于TCP协议的网络程序 下图是基于TCP协议的客户端/服务器程序的一般流程: 服务器调用socket().bind().listen()完成初始化后,调用accept()阻塞等待,处于监听端口的 ...
- UNP学习笔记1——基本TCP套接字编程
1 套接字地址结构 大多数套接字函数都需要一个指向套接字地址结构的指针作为参数.每个协议族都定义了自己的套接字结构.这些套接字的结构以sockaddr_开头,以每个协议族唯一的后缀名结尾. 1.1 I ...
- TCP套接字编程模型及实例
摘要: 本文讲述了TCP套接字编程模块,包括服务器端的创建套接字.绑定.监听.接受.读/写.终止连接,客户端的创建套接字.连接.读/写.终止连接.先给出实例,进而结合代码分析. PS:本文权当 ...
- Python网络编程之TCP套接字简单用法示例
Python网络编程之TCP套接字简单用法示例 本文实例讲述了Python网络编程之TCP套接字简单用法.分享给大家供大家参考,具体如下: 上学期学的计算机网络,因为之前还未学习python,而jav ...
- Python黑帽编程2.8 套接字编程
Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...
- unix网络编程第四章----基于TCP套接字编程
为了执行网络I/O操作.进程必须做的第一件事情就是调用Socket函数.指定期待的通信协议 #include<sys/socket.h> int socket(int family,int ...
- 【UNIX网络编程(二)】基本TCP套接字编程函数
基于TCP客户/server程序的套接字函数图例如以下: 运行网络I/O.一个进程必须做的第一件事就是调用socket函数.指定期望的通信协议类型. #include <sys/socket.h ...
- unix网络编程——TCP套接字编程
TCP客户端和服务端所需的基本套接字.服务器先启动,之后的某个时刻客户端启动并试图连接到服务器.之后客户端向服务器发送请求,服务器处理请求,并给客户端一个响应.该过程一直持续下去,直到客户端关闭,给服 ...
随机推荐
- foreach循环时动态往数组里添加数据
今天在用TP做项目的时候遇到一个问题,foreach的时候需要动态往数组里添加数据,示例代码如下: $arr = array( array('id'=>'字符串1','name'=>'字符 ...
- how to solve "[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!"
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- Oracle session active 和 inactive 状态 说明
Oracle session active 和 inactive 状态 说明 原创 2011年06月12日 13:08:00 标签: session / oracle / database / ser ...
- WebLogic Operator初试
时隔几个月,重拾WebLogic 为什么是WebLogic 简单说一句就是,因为WebLogic在中间件里面够复杂. Server不同的角色 AdminServer和Managed Server之间的 ...
- mysql将字符串字段转为数字排序或比大小
SELECT * FROM Student WHERE 1 = 1 ORDER BY -ID DESC ; SELECT * FROM Student WHERE 1 = 1 ORDER BY (ID ...
- 微信自动抢红包android实现
AccessibilityService-微信自动抢红包 2018年02月01日 16:09:06 阅读数:1757 在领导发红包的时候,看到有些同事在1s.2s抢到红包,为什么他们能够这么快?一定是 ...
- 17、percona-toolkit
pt-ioprofile工具:1.percona-toolkit(在http://www.percona.com下载)2.安装该工具依赖的perl组件yum install perl-IO-Socke ...
- Struts2 中#、@、%和$符号的用途
一.#符号的用途一般有三种. “#”主要有三种用途: 1. 访问OGNL上下文和Action上下文,#相当于ActionContext.getContext():下表有几个ActionContext中 ...
- VLAN讲解
VLAN(Virtual Local Area Network)的中文名为"虚拟局域网".VLAN是一种将局域网设备从逻辑上划分成一个个网段,从而实现虚拟工作组的新兴数据交换技术. ...
- Linux 进程学习
1.linux进程间通讯 继承unix进程间通讯:管道 信号 AT&T :system V IPC 通讯进程只能在单个计算机 :信号量 消息队列 共享内存 ...