关键词:
socket编程介绍
Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,可以用它们来开发TCP/IP网络上的应用程序。
Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
常用的Socket类型有两种:流式Socket (SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
client/server通信模型
在客户/服务器模式中我们将请求服务的一方称为客户(client),将提供某种服务的一方称为服务器(server)。
一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“唤醒”并且为客户提供服务—对客户的请求作出适当的反应。
套接字
套接字是一个通信终结点,它是Socket应用程序用来在网络上发送或接收数据包的对象。
套接字具有类型,与正在运行的进程相关联,并且可以有名称。
套接字一般只与使用网际协议组的同一“通信域”中的其他套接字交换数据。
流式套接字编程流程
客户端
1.创建套接字
client_sockfd=socket(PF_INET,SOCK_STREAM,0)
用socket函数创建套接字,返回套接字描述符
2.定义服务器端地址
struct sockaddr_in remote_addr; //服务器端网络地址结构体memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零 remote_addr.sin_family=AF_INET; //设置为IP通信 remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址 remote_addr.sin_port=htons(8000); //服务器端口
3.与服务器建立连接
connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr)
用于tcp/ip应用的地址结构类型是socketsddr_in,而套接字socket中应用的地址类型为socketaddr,所以需要进行强制类型转换。
4.发送接收数据
len=send(client_sockfd,buf,strlen(buf),0); len=recv(client_sockfd,buf,BUFSIZ,0);
发送数据时已知数据的长度,用strlen(buf)计算;而接收数据时不知道接收数据的长度,就必须将接收长度设为定义的数组的最大值,再通过接收函数的返回值来确定数组的实际大小。
5.关闭套接字
close(client_sockfd);//关闭套接字
没有数据可发送就关闭套接字
服务器端
1.创建套接字,定义自身的地址
int server_sockfd=socket(PF_INET,SOCK_STREAM,0);//服务器端套接字
struct sockaddr_in my_addr; //服务器网络地址结构体 memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零 my_addr.sin_family=AF_INET; //设置为IP通信 my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上 my_addr.sin_port=htons(8000); //服务器端口号
2.将套接字与地址绑定
bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)
3.监听是否有客户请求
listen(server_sockfd,5);
4接受客户端请求
client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr,&sin_size)
accept函数返回新的连接的套接字描述符。为每个新的连接请求创建了一个新的套接字,服务器只对新的连接使用该套接字,原来的监听套接字接受其他的连接请求。新的连接上传输数据使用新的套接字,使用完毕,服务器将关闭这个套接字。
5.接收并发送数据
while((len=recv(client_sockfd,buf,BUFSIZ,0))>0) buf[len]=\'\\0\'; printf("recieved:%s\\n",buf); printf("Enter string to send: "); scanf("%s",buf); if(send(client_sockfd,buf,strlen(buf),0)<0) perror("write"); return 1;
6关闭套接字
close(client_sockfd);
close(server_sockfd);
实现代码
客户端
1 #include <stdio.h> 2 #include <string.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <unistd.h> 8 9 int main(int argc, char *argv[]) 10 11 int client_sockfd; 12 int len; 13 struct sockaddr_in remote_addr; //服务器端网络地址结构体 14 char buf[BUFSIZ]; //数据传送的缓冲区 15 memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零 16 remote_addr.sin_family=AF_INET; //设置为IP通信 17 remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址 18 remote_addr.sin_port=htons(8000); //服务器端口号 19 20 /*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/ 21 if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0) 22 23 perror("socket"); 24 return 1; 25 26 27 /*将套接字绑定到服务器的网络地址上*/ 28 if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0) 29 30 perror("connect"); 31 return 1; 32 33 printf("connected to server\\n"); 34 len=recv(client_sockfd,buf,BUFSIZ,0);//接收服务器端信息 35 buf[len]=\'\\0\'; 36 printf("%s",buf); //打印服务器端信息 37 38 /*循环的发送接收信息并打印接收信息--recv返回接收到的字节数,send返回发送的字节数*/ 39 while(1) 40 41 printf("Enter string to send:"); 42 scanf("%s",buf); 43 if(!strcmp(buf,"quit")) 44 break; 45 len=send(client_sockfd,buf,strlen(buf),0); 46 len=recv(client_sockfd,buf,BUFSIZ,0); 47 buf[len]=\'\\0\'; 48 printf("received:%s\\n",buf); 49 50 close(client_sockfd);//关闭套接字 51 return 0; 52
服务器端
1 #include <stdio.h> 2 #include <string.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <unistd.h> 8 9 int main(int argc, char *argv[]) 10 11 int server_sockfd;//服务器端套接字 12 int client_sockfd;//客户端套接字 13 int len; 14 struct sockaddr_in my_addr; //服务器网络地址结构体 15 struct sockaddr_in remote_addr; //客户端网络地址结构体 16 int sin_size; 17 char buf[BUFSIZ]; //数据传送的缓冲区 18 memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零 19 my_addr.sin_family=AF_INET; //设置为IP通信 20 my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上 21 my_addr.sin_port=htons(8000); //服务器端口号 22 23 /*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/ 24 if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0) 25 26 perror("socket"); 27 return 1; 28 29 30 /*将套接字绑定到服务器的网络地址上*/ 31 if (bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0) 32 33 perror("bind"); 34 return 1; 35 36 37 /*监听连接请求--监听队列长度为5*/ 38 listen(server_sockfd,5); 39 40 sin_size=sizeof(struct sockaddr_in); 41 42 /*等待客户端连接请求到达*/ 43 if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr,&sin_size))<0) 44 45 perror("accept"); 46 return 1; 47 48 printf("accept client %s\\n",inet_ntoa(remote_addr.sin_addr)); 49 len=send(client_sockfd,"Welcome to my server\\n",21,0);//发送欢迎信息 50 51 /*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/ 52 while((len=recv(client_sockfd,buf,BUFSIZ,0))>0) 53 54 buf[len]=\'\\0\'; 55 printf("recieved:%s\\n",buf); 56 printf("Enter string to send: "); 57 scanf("%s",buf); 58 if(send(client_sockfd,buf,strlen(buf),0)<0) 59 60 perror("write"); 61 return 1; 62 63 64 close(client_sockfd); 65 close(server_sockfd); 66 return 0; 67
运行结果
跟踪分析到系统调用
当用户进程使用socket API 的时候,会产生向量为0x80的编程异常,系统执行系统调用。
进程传递系统调用号到寄存器eax,指明需要哪个系统调用,同时会将系统调用需要的参数存入相关寄存器。
系统调用处理函数system_call是Linux中所有系统调用的入口点,通过进程存在eax寄存器中的系统调用号决定调用哪个系统调用,而所有的socket系统调用的总入口时sys_socketcall()。
对如下代码跟踪分析
int socket(int domain, int type, int protocol);
用户进程将sys_socket()的系统调用号198存入eax中,并将socket()函数中的三个参数分别存入ebx,ecx,edx寄存器,产生一个0x80的编程异常。系统调用处理函数根据系统调用号,执行sys_socket()。
sys_socket在内核中的源码为:
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) int retval; struct socket *sock; int flags; ... retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); if (retval < 0) goto out_release; out: /* It may be already another descriptor 8) Not kernel problem. */ return retval; out_release: sock_release(sock); return retval;
可以看到,socket函数主要由sock_create和sock_map_fd这两个函数完成。
sock_create函数用于创建socket。sock_create() 实际调用的是 __sock_create(),其中比较重要的是sock_alloc()和pf->create()两个函数。
sock_map_fd函数用于得到一个文件号。这个函数主要有两个部分,一个是创建file文件结构,fd文件描述符,另一部分是将file文件结构和fd文件描述符关联,同时将上一步返回的socket也一起绑定,形成一个完整的逻辑。
socket系统调用的操作概述为:首先在内核生成一个socket_alloc 和tcp_sock类型的对象,其中sock_alloc对象中的socket和tcp_sock对象的sock绑定,sock_alloc对象中的inode和file类型对象绑定。然后将分配的文件描述符fd和file对象关联,最后将这个文件描述符fd返回给用户使用。
利用java的socket实现一个简单hello/hi聊天程序(代码片段)
利用java的Socket实现一个简单hello/hi聊天程序 首先,我们来用java实现一个简单的hello/hi聊天程序。在这个程序里,我学习到了怎么用socket套接套接字来进行编程。简单理解了一些关于socket套接字和底层调用的关系。关于jav... 查看详情
使用java实现一个hello/hi的简单的网络聊天程序(代码片段)
1、socket原理 Socket实质上提供了进程通信的端点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。 &n... 查看详情
使用python完成一个hello/hi的简单的网络聊天程序(代码片段)
...章中,我将先简要介绍socket原理,然后给出一个利用Python实现的简单通信样例,最后通过跟踪系统调用来分析Python中socket函数与Linux系统调用的对应关系。1.socket简介Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组... 查看详情
java实现一个hello/hi的简单的网络聊天程序(代码片段)
使用Java的Socket实现客户端和服务器端之间的连接,实现客户端重复发送数据到服务器端的功能。即,用户可以在控制台不断输入内容,并将内容逐一发送给服务端。并在服务端显示。socket定义 网络上的两个程... 查看详情
一个hello/hi的简单的网络聊天程序(代码片段)
我选择使用python来实现hello/hi的简单网络聊天程序,源代码包括两个部分,客户端代码和服务器端代码,源代码部分如下图所示:服务器端代码1importsocket23HOST=‘127.0.0.1‘4PORT=888856server=socket.socket()7server.bind((HOST,PORT))8server.list... 查看详情
使用python实现一个hello/hi的简单的网络聊天程序(代码片段)
一、TCP/IP协议通信原理 TCP/IP协议包含的范围非常的广,它是一种四层协议,包含了各种硬件、软件需求的定义。TCP/IP协议确切的说法应该是TCP/UDP/IP协议。UDP协议(UserDatagramProtocol用户数据报协议),是一种保护消息边界的... 查看详情
以您熟悉的编程语言为例完成一个hello/hi的简单的网络聊天程序
在这片博文我们将使用python完成一个hello/hi的简单的网络聊天程序 先做一下准备工作 1.linux的socket基础api: 使用socket()创建套接字intsocket(intaf,inttype,intprotocol);af为IP地址类型,AF_INE和AF_INET6分别对应ipv4和ipv6地址type... 查看详情
基于python完成一个hello/hi的简单的网络聊天程序(代码片段)
...序进行通信。网络套接字是IP地址与端口的组合。传输层实现端到端的通信,因此,每一个传输层连接有两个端点。那么,传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的 查看详情
一个简单的hello/hi的网络聊天程序(代码片段)
TCP套接字函数了解socket函数为了执行网络I/O,一个进程必须做的第一件事情就是调用socket函数,指定期望的通信协议类型(使用ipv4的TCP、使用ipv6的UDP、Unix域字节流协议等)#include<sys/socket.h>intsocket(intfamily,inttype,intprotocol);返... 查看详情
用c++完成一个hello/hi的简单的网络聊天程序(代码片段)
1.什么是Socket 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络... 查看详情
一个hello/hi的简单的网络聊天程序和pythonsocketapi与linuxsocketapi之间的关系(代码片段)
1.Socket概述 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套... 查看详情
在windows环境下用c++完成一个hello/hi网络聊天程序(代码片段)
...了解了socket网络程序编程,接下来,就要学以致用,完成一个hello/hi的网络聊天程序。 Socket介绍 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就... 查看详情
hello/hi的简单的网络聊天程序(代码片段)
... 在Internet层,解析IP地址,寻找通往目标IP的目的地的下一个路由地址。在网络接口层,则是寻找响应的硬件(MAC)地址。数据流以及网络拓扑结构如下图所示。 数据流以及网络拓扑结构 我们知道两个进程如果需要进... 查看详情
实现一个的简单的网络聊天程序(代码片段)
本次实验采用Java语言,编写了一个简单的聊天室程序,可以实现多人之间的聊天。以下将对该程序进行详尽分析,并对比分析该编程语言提供的网络接口API与LinuxSocketAPI之间的关系。1、网络通信相关要素1)协议 通信的... 查看详情
python实现一个简单的网络聊天程序(代码片段)
...利套接字)伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信。BSDSocket的应用编程接口已经是网络套接字的抽象标准。大多数其他程序语言使用一种相似的编程接口。由于伯克利... 查看详情
求c语言高手,实现一个简单的tcpip程序以实现两台计算机之间的聊天通信,
...服务器可以与一个或多个客户端连接要求:1)使用C或C++语言2)利用TCPsocket实现3)可以在windows或Linux/unix下调试4)不做界面,直接用显示在console窗口上5)必须有足够的注释老师给了结构//tcpsever.cpp:Definestheentrypointfortheconsoleapplic... 查看详情
go语言实现简单的聊天室(代码片段)
通常聊天室的架构分为服务器端和客户端:服务器端:接受来自于客户端的连接请求并建立连接;所有客户端的连接会放进连接池中,用于广播消息;客户端:连接服务器;向服务器发送消息;接收服务器的广播消息;注意事项... 查看详情
用go语言实现一个简单的聊天机器人(代码片段)
一、介绍目的:使用Go语言写一个简单的聊天机器人,复习整合Go语言的语法和基础知识。软件环境:Go1.9,Goland2018.1.5。 二、回顾Go语言基本构成要素:标识符、关键字、字面量、分隔符、操作符。它们可以组成各种表达式... 查看详情