复习贴,资料大多来自百科.看了一遍理解了一遍,把绕口的话按语义给改了`_>`

对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。

用户开始Socket连接时, 首先申请一个Socket号码;同时要知道对方的号码. 然后向对方发出连接请求(假如对方不在同一局域网相当于给出网络地址). 假如对方在场并空闲(相当于通信的另一主机开机且可以接受连接请求),双方就可以Socket通信, 连接成功。双方建立连接通讯过程,向Socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭Socket,撤消连接。


连接过程

连接的三次握手:服务器监听,客户端请求,连接确认。
(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
(2)客户端请求:客户端发起连接请求,请求服务端的Socke. 客户端要先确认服务器的套接字,指出服务器端Socket的地址和端口号,然后发起连接请求。
(3)连接确认:当服务器端监听到客户端的连接请求, 它就响应客户端请求建立一个新的线程. 把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

 
实现函数
 
创建一个Socket:
int socket(int domain, int type, int protocol);
参数说明:
  
domain:协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定Socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式Socket(SOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。
protocol:指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
注意:1.type和protocol不可以随意组合,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当第三个参数为0时,会自动选择第二个参数类型对应的默认协议。
2.WindowsSocket下protocol参数中不存在IPPROTO_STCP
  
返回值:调用成功返回创建的Socket id. 失败返回INVALID_SOCKET(Linux下失败返回-1). 
每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。

绑定

函数原型:
int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);
参数说明:
socket:是一个套接字描述符。
address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。
address_len:确定address缓冲区的长度。
返回值:
如果函数执行成功,返回值为0,否则为SOCKET_ERROR。

接收

函数原型:
int recv(SOCKET socket, char FAR* buf, int len, int flags);
参数说明: 
socket:一个标识已连接套接口的描述字。
buf:用于接收数据的缓冲区。
len:缓冲区长度。
flags:指定调用方式。取值:MSG_PEEK 查看当前数据,数据将被复制到缓冲区中,但并不从输入队列中删除;MSG_OOB 处理带外数据。
返回值:
若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
函数原型:
ssize_t recvfrom(int sockfd, void buf, int len, unsigned int flags, struct socketaddr* from, socket_t* fromlen);
参数说明:
sockfd:标识一个已连接套接口的描述字。
buf:接收数据缓冲区。
len:缓冲区长度。
flags:调用操作方式。是以下一个或者多个标志的组合体,可通过or操作连在一起:
(1)MSG_DONTWAIT:操作不会被阻塞;
(2)MSG_ERRQUEUE: 指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。错误以sock_extended_err结构形态被使用。
(3)MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。
(4)MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。
(5)MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。
(6)MSG_EOR:指示记录的结束,返回的数据完成一个记录。
(7)MSG_TRUNC:指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间。
  
/*(MSG_TRUNC使用错误,4才是MSG_TRUNC的正确解释)*/
(8)MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。
(9)MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。
(10)MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。
from:(可选)指针,指向装有源地址的缓冲区。
fromlen:(可选)指针,指向from缓冲区长度值。

发送

函数原型:
int sendto( SOCKET s, const char FAR* buf, int size, int flags, const struct sockaddr FAR* to, int tolen);
参数说明:
s:套接字
buf:待发送数据的缓冲区
size:缓冲区长度
flags:调用方式标志位, 一般为0, 改变Flags,将会改变Sendto发送的形式
addr:(可选)指针,指向目的套接字的地址
tolen:addr所指地址的长度
返回值:
如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。

接收连接请求

函数原型:
int accept( int fd, struct socketaddr* addr, socklen_t* len);
参数说明:
fd:套接字描述符。
addr:返回连接着的地址
len:接收返回地址的缓冲区长度
返回值:
成功返回客户端的文件描述符,失败返回-1。
 

 
Linux下的Socket
/* File Name: server.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define DEFAULT_PORT 8000 //默认端口
#define MAXLINE 4096 //最大行数
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char buff[];
int n;
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, )) == - ) //流式Socket(SOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。
   {
  printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
  exit();
}
//初始化
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET; //AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。 htonl,host to network, l 的意思是返回类型是long  将主机数转换成无符号长整形的网络字节顺序。
servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT htons将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian) //将本地地址绑定到所创建的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -)
   {
  printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
  exit();
}
//开始监听是否有客户端连接
if( listen(socket_fd, ) == -)
  {
  printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
  exit();
}
printf("======waiting for client's request======\n");
while()
  {
//阻塞直到有客户端连接,不然多浪费CPU资源。
  if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -)
     {
  printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
  continue;
  }
//接受客户端传过来的数据
  n = recv(connect_fd, buff, MAXLINE, );
//向客户端发送回应数据
  if(!fork())
  { /*子进程*/
  if(send(connect_fd, "Hello,you are connected!\n", ,) == -)
  perror("send error");
  close(connect_fd);
  exit();
  }
  buff[n] = '\0';
  printf("recv msg from client: %s\n", buff);
  close(connect_fd);
}
close(socket_fd);
}
/* File Name: client.c */  

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h> #define MAXLINE 4096 int main(int argc, char** argv)
{
int sockfd, n,rec_len;
char recvline[], sendline[];
char buf[MAXLINE];
struct sockaddr_in servaddr;

if( argc != )
   {
  printf("usage: ./client <ipaddress>\n");
  exit();
} if( (sockfd = socket(AF_INET, SOCK_STREAM, )) < )
   {
  printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
  exit();
} memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
if( inet_pton(AF_INET, argv[], &servaddr.sin_addr) <= )
  {
  printf("inet_pton error for %s\n",argv[]);
  exit();
  }
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
  {
  printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
  exit();
}
printf("send msg to server: \n");
fgets(sendline, , stdin);
if( send(sockfd, sendline, strlen(sendline), ) < )
{
  printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
  exit();
}
if((rec_len = recv(sockfd, buf, MAXLINE,)) == -)
  {
perror("recv error");
exit();
}
buf[rec_len] = '\0';
printf("Received : %s ",buf);
close(sockfd);
exit();
}
 
 
 
Windows下的Socket
//Server服务端
#include<winsock2.h>
#include<stdio.h>
#pragmacomment(lib,"ws2_32.lib")
void main()
{
WSADATA wsaData;
SOCKET sockServer;
SOCKADDR_IN addrServer;
SOCKET sockClient;
SOCKADDR_IN addrClient;
WSAStartup(MAKEWORD(,),&wsaData);
sockServer=socket(AF_INET,SOCK_STREAM,);
addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//INADDR_ANY表示任何IP
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons();//绑定端口6000
bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR)); //Listen监听端
listen(sockServer,);//5为等待连接数目
printf("服务器已启动:\n监听中...\n");
int len=sizeof(SOCKADDR);
charsendBuf[];//发送至客户端的字符串
charrecvBuf[];//接受客户端返回的字符串 //会阻塞进程,直到有客户端连接上来为止
sockClient=accept(sockServer,(SOCKADDR*)&addrClient,&len);
//接收并打印客户端数据
recv(sockClient,recvBuf,,);
printf("%s\n",recvBuf); //关闭socket
closesocket(sockClient);
WSACleanup();}
//Client客户端
#include<winsock2.h>
#include<stdio.h>
#pragmacomment(lib,"ws2_32.lib")
void main()
{
WSADATA wsaData;
SOCKET sockClient;//客户端Socket
SOCKADDR_IN addrServer;//服务端地址
WSAStartup(MAKEWORD(,),&wsaData);
//新建客户端socket
sockClient=socket(AF_INET,SOCK_STREAM,);
//定义要连接的服务端地址
addrServer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//目标IP(127.0.0.1是回送地址)
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons();//连接端口6000
//连接到服务端
connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
//发送数据
charmessage[]="HelloSocket!";
send(sockClient,message,strlen(message)+,);
//关闭socket
closesocket(sockClient);
WSACleanup();}

 
三次握手与四次断开<转>
连接握手:
.客户端发送建立连接请求
.服务端确认连接请求
.客户端确认已经连接
以上3步完成后即可使用send recv了
断开握手:如果要正确断开必须经过4次握手。
.客户端发送请求停止TCP连接请求
.服务端收到请求后将这一个socket关闭
.服务端发送反向请求客户端关闭socket连接
.客户端确认服务端请求,关闭socket连接。 TCP是一个面向连接的服务,面向连接的服务是电话系统服务模式的抽象,每一次完整的数据传输都必须经过建
立连接,数据传输和终止连接3个过程,TCP建立连接的过程称为三次握手,下面看一下三次握手的具本过程
TCP三次握手过程
主机A通过向主机B 发送一个含有同步序列号的标志位的数据段给主机B ,向主机B 请求建立连接,通过这个数据段,
主机A告诉主机B 两件事:我想要和你通信;你可以用哪个序列号作为起始数据段来回应我.
主机B 收到主机A的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉主机A两件事:
我已经收到你的请求了,你可以传输数据了;你要用哪佧序列号作为起始数据段来回应我
主机A收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:"我已收到回复,我现在要开始传输实际数据了 这样3次握手就完成了,主机A和主机B 就可以传输数据了.
3次握手的特点
没有应用层的数据
SYN这个标志位只有在TCP建产连接时才会被置1
握手完成后SYN标志位被置0 TCP建立连接要进行3次握手,而断开连接要进行4次,这是由于TCP的半关闭造成的,因为TCP连接是全双工的(
即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭,这个单方向的关闭就叫半关闭.
关闭的方法是一方完成它的数据传输后,就发送一个FIN来向另一方通告将要终止这个方向的连接.当一端收到一个FIN,它必须
通知应用层TCP连接已终止了这个方向的数据传送,发送FIN通常是应用层进行关闭的结果. 4次断开
当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求
主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1
由B 端再提出反方向的关闭请求,将FIN置1
主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束.
由TCP的三次握手和四次断开可以看出,TCP使用面向连接的通信方式,大大提高了数据通信的可靠性,使发送数据端
和接收端在数据正式传输前就有了交互,为数据正式传输打下了可靠的基础

TODO:Socket的几个IO模型

Socket通讯的更多相关文章

  1. 闲来无事,写个基于TCP协议的Socket通讯Demo

    .Net Socket通讯可以使用Socket类,也可以使用 TcpClient. TcpListener 和 UdpClient类.我这里使用的是Socket类,Tcp协议. 程序很简单,一个命令行 ...

  2. 试解析Tomcat运行原理(一)--- socket通讯

    关于这篇文章也确实筹划了很久,今天决定开篇写第一篇,说起tomcat首先很容易联想到IIS,因为我最开始使用的就是.net技术,我第一次使用asp写学生成绩管理系统后,很茫然如何让别人都能看到或者说使 ...

  3. c# TCP Socket通讯基础

    在做网络通讯方面的程序时,必不可少的是Socket通讯. 那么我们需要有一套既定的,简易的通讯流程. 如下: <pre name="code" class="csh ...

  4. Android笔记:Socket通讯常见问题

    经验证的socket通讯问题 1.如果是模拟器和本机PC直接通讯,需要使用本机IP地址 而不是 10.0.2.2  如本机的静态地址为192.168.1.2 则直接使用该地址 2.接收和连接代码不能在 ...

  5. java socket通讯(二)处理多个客户端连接

    通过java socket通讯(一) 入门示例,就可以实现服务端和客户端的socket通讯,但是上一个例子只能实现一个服务端和一个客户端之间的通讯,如果有多个客户端连接服务端,则需要通过多线程技术来实 ...

  6. java socket通讯(一) 入门示例

    一.入门 要想学习socket通讯,首先得知道tcp/ip和udp连接,具体可参考浅谈TCP/IP 和 UDP的区别 二.示例 首先新建了一个java工程,包括两个部分,客户端SocketClient ...

  7. Socket网络通讯开发总结之:Java 与 C进行Socket通讯 + [备忘] Java和C之间的通讯

    Socket网络通讯开发总结之:Java 与 C进行Socket通讯 http://blog.sina.com.cn/s/blog_55934df80100i55l.html (2010-04-08 ...

  8. 利用BlazeDS的AMF3数据封装与Flash 进行Socket通讯

    前几天看到了Adobe有个开源项目BlazeDS,里面提供了Java封装AMF3格式的方法.这个项目貌似主要是利用Flex来Remoting的,不过我们可以利用他来与Flash中的Socket通讯. ...

  9. .Net中的Socket通讯

    .NetFrameWork为Socket通讯提供了System.Net.Socket命名空间,在这个命名空间里面有以下几个常用的重要类分别是: ·Socket类 这个低层的类用于管理连接,WebReq ...

  10. Linux网络服务器epoll模型的socket通讯的实现(一)

    准备写一个网络游戏的服务器的通讯模块,参考网上看到的一些代码,在linux下面实现一个多线程的epoll模型的socket通讯的代码,以下是第一部分多线程的切换代码: 1 #include <s ...

随机推荐

  1. oracle迁移postgres之-Ora2Pg

    描述 Ora2Pg:甲骨文PostgreSQL数据库模式转换器是一个免费的工具用于Oracle数据库迁移到PostgreSQL兼容模式.它连接Oracle数据库,扫描它自动提取其结构或数据,然后生成S ...

  2. The Number Off of FFF

    X soldiers from the famous “FFF army'' is standing in a line, from left to right. You, as the captai ...

  3. PRINCE2的思维结构

    PRINCE2的思维结构   PRINCE是PRoject IN Controlled Environment(受控环境下的项目管理)的简称.PRINCE2是这种方法的第二个重要版本,由英国政府商务部 ...

  4. 亚马逊EC2弹性IP收费

    刚收到亚马逊云账单,被扣了4.44刀,吓尿,麻溜的去查账单详情,发现之前弃用的美国实例有个弹性IP没释放掉,占着茅坑还不拉屎,被扣钱宝宝无话可说,赶紧释放... $0.005 per Elastic ...

  5. NOI 动态规划题集

    noi 1996 登山 noi 8780 拦截导弹 noi 4977 怪盗基德的滑翔翼 noi 6045 开餐馆 noi 2718 移动路线 noi 2728 摘花生 noi 2985 数字组合 no ...

  6. Bar codes in NetSuite Saved Searches(transport/reprint)

    THIS IS A COPY FROM BLOG Ways of incorporating Bar Codes into your Netsuite Saved Searches.    Code ...

  7. [翻译]lithium 安装

    安装 要求 web服务器 你需要一个web服务器来运行你的应用,最好是可以运行在你的本地机器上(你所有的开发不是都在这上面做的吗,不是吗?不是吗?).对于PHP而言,框架在很多web服务器上都运行的很 ...

  8. 通知Notification

    步骤: 1.调用getSystemService()获取NotificationManager:NotificationManager manager = (NotificationManager)g ...

  9. 【转载】我眼中的Oracle性能优化

    我眼中的Oracle性能优化 大家对于一个业务系统的运行关心有如下几个方面:功能性.稳定性.效率.安全性.而一个系统的性能有包含了网络性能.应用性能.中间件性能.数据库性能等等. 今天从数据库性能的角 ...

  10. 1. C语言中的数据结构.md

    C语言内建数据结构类型 整型 整型数据是最基本的数据类型,不过从整形出发衍生出好几种integer-like数据结构,譬如字符型,短整型,整型,长整型.他们都是最基本的方式来组织的数据结构,一般是几位 ...