为了简化开发通信程序的工作,由Berkely学校开发了一套网络通信程序的API函数标准 socket标准被扩展成window socket和unix socket linux中的网络编程通过socket接口实现。Socket既是一种特殊的IO,它也是一种文件描述符。一个完整的Socket 都有一个相关描述{协议,本地地址,本地端口,远程地址,远程端口};每一个Socket 有一个本地的唯一Socket 号,由操作系统分配。
1、初始化主机通过一个同步标志置位的数据段发出会话请求。 2、接收主机通过发回具有以下项目的数据段表示回复:同步标志置位、即将发送的数据段的起始字节的顺序号、应答并带有将收到的下一个数据段的字节顺序号。 3、请求主机再回送一个数据段,并带有确认顺序号和确认号
流式套接字(SOCK_STREAM) 流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议。TCP 保证了数据传输的正确性和顺序性
socket模拟客户端
以下是实现代码:
#include
#include
#include
#include
#include
#include
#include /* for struct sockaddr_in*/
#include
#include
//关于客户端的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("run program+ip+port\n");
return-1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//连接服务器,设置服务器的地址(ip和端口)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr= inet_addr(argv[1]);
svraddr.sin_port=htons(atoi(argv[2]));
int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("connect error");
}
char buf[1024]={0};
int rdsize =read(sockfd,buf,1024);
printf("read size %d,data=%s\n",rdsize,buf);
return 0;
}
socket模拟服务端
以下实现代码:
#include
#include
#include
#include
#include
#include
#include
#include /* for struct sockaddr_in*/
#include
#include
//关于服务器的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//绑定地址(ip和端口号)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr=INADDR_ANY;
//svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");//第二种写法
svraddr.sin_port=htons(5555);//大于1024,系统用的都是小于1024
int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("bind error");
}
//设置监听参数back login,半连接数的最大
ret=listen(sockfd,1024);
if(ret<0)
{
error_exit("listen error");
}
struct sockaddr_in removeaddr;
int addr_len=sizeof(removeaddr);//一定要是长度,不能为0
while(1)
{
//返回一个和客户端通信的文件描述符
int fd=accept(sockfd,(struct sockaddr*)&removeaddr,&addr_len);
//TCP读写规则
//1.读的时候没有数据,进去阻塞
//2.如果关闭,读取返回0
//3.如果关闭,写入数据会产生SIGPIPE
int wdsize=write(fd,"helloworld",strlen("helloworld"));
printf("write size=%d\n",wdsize);
}
return 0;
}
好了,实现以上代码,我们进行结果实现:
我们先执行服务端代码,正在写入中......
接下来执行客服端代码,第一个参数为我们的ip号,5555为我们设置的端口
最后服务端显示:
下面我们来验证一下流式套接字的特点
socket之流式套接字是这样描述的,流式套接字(SOCK_STREAM) 流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议。TCP 保证了数据传输的正确性和顺序性
上面这句话的意思其实是说流式套接字传输数据的时候其实像管道一样,数据像流水一样,也就是粘包
我们将服务器的代码写入的时候,连续写入三次helloworld
以下完整代码:
#include
#include
#include
#include
#include
#include
#include
#include /* for struct sockaddr_in*/
#include
#include
//关于服务器的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//绑定地址(ip和端口号)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr=INADDR_ANY;
//svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");//第二种写法
svraddr.sin_port=htons(5555);//大于1024,系统用的都是小于1024
int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("bind error");
}
//设置监听参数back login,半连接数的最大
ret=listen(sockfd,1024);
if(ret<0)
{
error_exit("listen error");
}
struct sockaddr_in removeaddr;
int addr_len=sizeof(removeaddr);//一定要是长度,不能为0
while(1)
{
//返回一个和客户端通信的文件描述符
int fd=accept(sockfd,(struct sockaddr*)&removeaddr,&addr_len);
//TCP读写规则
//1.读的时候没有数据,进去阻塞
//2.如果关闭,读取返回0
//3.如果关闭,写入数据会产生SIGPIPE
int wdsize=write(fd,"helloworld",strlen("helloworld"));
wdsize=write(fd,"helloworld",strlen("helloworld"));
wdsize=write(fd,"helloworld",strlen("helloworld"));
printf("write size=%d\n",wdsize);
}
return 0;
}
我们将客服端的代码加一个sleep,读的时候延迟一下
以下完整代码
#include
#include
#include
#include
#include
#include
#include /* for struct sockaddr_in*/
#include
#include
//关于客户端的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("run program+ip+port\n");
return-1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//连接服务器,设置服务器的地址(ip和端口)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr= inet_addr(argv[1]);
svraddr.sin_port=htons(atoi(argv[2]));
int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("connect error");
}
sleep(1);
char buf[1024]={0};
int rdsize =read(sockfd,buf,1024);
printf("read size %d,data=%s\n",rdsize,buf);
return 0;
}
跟上面的步骤执行一遍,得以下结果
我们发现三个helloworld是连续的,这就验证了流式套接字的特点,如同管道一般传输数据