套接字是通信端点的抽象。文件描写叙述符用open函数创建,而套接字描写叙述符用socket函数创建。socket函数原型例如以下:
int socket(int domain, int type, int protocol);
// 返回值:成功返回套接字描写叙述符,失败返回-1

domain域确定通信特性。不同的域表示地址的格式不同,表示域的常数以AF开头。表示地址族(address family):

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmVzdGxlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

type确定套接字类型。进一步确定通信特征:



protocol通常为0,表示按上述两个參数确定默认的协议。比如:
  • 通信域为AF_INET。套接字类型为SOCK_STREAM。则默认协议为TCP。

  • 通信域为AF_INET。套接字类型为SOCK_DGRAM。则默认协议为UDP。

关闭套接字的一端可用shutdown函数:
int shutdown(int sockfd, int how);
// 返回值:成功返回0,失败返回-1

how为SHUT_RD,关闭读端,无法从套接字读取数据;how为SHUT_WR。关闭写端,无法通过套接字发送数据。

关闭套接字描写叙述符的方法和关闭文件描写叙述符的方法同样,都是使用close函数。


不同的CPU。使用不同的字节序列来表示数据,分大端序和小端序。比如大端序的机器发送到小端序的机器上,则接收到的数据顺序会颠倒。为了避免这样的情况,使网络传输与详细的机器类型无关,採用网络字节序。发送方把本机序转换为网络字节序后发送,接收方把接收到的网络字节序转换为本机序。

TCP/IP採用大端字节序,即网络字节序,而我们经常使用的X86结构是小端模式。下图来自维基百科,描写叙述得非常形象:

 
  

TCP/IP应用程序提供了四个通用函数用于本地字节序和网络字节序之间的转换:
  1. htonl:本地32位整型转网络字节序
  2. htons:本地16位整型转网络字节序
  3. ntohl:网络字节序转32位整型本地字节序
  4. ntohs:网络字节序转16位整型本地字节序

下面是TCP和UDP程序的相关函数接口的调用过程。
1、TCP服务端:
  • socket:建立一个传输端点。返回一个套接字描写叙述符。

  • bind:将套接字绑定到某个IP和port。此IP和port就作为监听对象。注意,因为系统会分配给client默认地址。所以client使用该函数意义不大。

  • listen:将套接字转换成监听套接字,使得client的连接请求可被内核接受,上述三步为设置监听描写叙述符的正常步骤。
  • accept:休眠。等待客户连接被内核接受。三次握手完成后返回一个新的套接字描写叙述符。该描写叙述符连接到调用connect的client。被称为已连接描写叙述符。
  • send:发数据。
  • recv:收数据。
  • close:关闭连接,引发四次握手。

2、TCPclient:
  • socket:创建套接字。
  • connect:建立连接。指明目的,和服务端的accept函数相应。

  • send:发数据。
  • recv:收数据。
3、UDP服务端:
  • socket:建立套接字。
  • bind:绑定监听的IP和port。
  • recvfrom:堵塞等待数据的到来。

4、UDPclient:
和TCPclient基本一致。假设在发送之前使用了connect函数。那么发送报文的目的地址都是connect调用中所指定的地址。所以直接send函数进行发送。

没用之前没有调用connect函数,发送用sendto函数。这个函数须要在參数中指定目的地址。

下面是一些例程。

TCPclient:
/* 基于TCP协议的socket编程client代码 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* client:
* socket-->connect-->send/recv
*/ #define SERVER_PORT 8888 int main(int argc, char **argv)
{
int iRet;
int iSocketClient;
struct sockaddr_in tSocketServerAddr; int iSendLen;
unsigned char ucSendBuf[1000]; if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
} /* 1. 建立socket */
iSocketClient = socket(AF_INET, SOCK_STREAM, 0); /* 2. 与服务器建立TCP连接 */
tSocketServerAddr.sin_family = AF_INET; /* recommend */
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net short */
if (inet_aton(argv[1], &tSocketServerAddr.sin_addr) == 0) /* 字符串转服务器IP */
{
printf("Invalid server IP\n");
return -1;
}
bzero(tSocketServerAddr.sin_zero, 8); /* 假设iSocketClient没有绑定地址。则connect会给调用者绑定一个默认地址 */
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (iRet == -1)
{
/* 连接失败 */
printf("connect error\n");
return -1;
} while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
/* send函数发送数据 */
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if (iSendLen == -1)
{
/* 函数出错 */
close(iSocketClient);
return -1;
}
}
} return 0;
}

TCP服务端:
/* 基于TCP协议的socket编程server端代码 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
/* server:
* socket-->bind-->listen-->accept-->send/recv
*/
#define SERVER_PORT 8888
#define BACKLOG 10
int main(int argc, char **argv)
{
int iRet;
int iAddrLen;
int iSocketSever;
int iNewSocketSever;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRecvLen;
unsigned char ucRecvBuf[1000];
/* 子进程死后,会发送SIGCHLD信号给父进程,
* 这里设置为忽略SIGCHLD信号
*/
signal(SIGCHLD,SIG_IGN);
/* 1. 建立socket
* AF_INET: IPv4 Internet protocols
* SOCK_STREAM: TCP协议
* 0: 通常赋值
*/
iSocketSever = socket(AF_INET, SOCK_STREAM, 0);
if (iSocketSever == -1)
{
printf("socket error\n");
return -1;
}
/* 2. 配置socket
* 设置监听本机的哪个IP,端口
*/
tSocketServerAddr.sin_family = AF_INET; /* recommend */
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net short */
tSocketServerAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* 绑定到本机全部网络接口 */
bzero(tSocketServerAddr.sin_zero, 8);
iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (iRet == -1)
{
printf("bind error\n");
return -1;
}
/* 3. 开启socket监听模式
* 建立服务请求队列,BACKLOG为最大连接数
*/
iRet = listen(iSocketSever, BACKLOG);
if (iRet == -1)
{
printf("listen error\n");
return -1;
}
while (1)
{
/* 4. 休眠,等待建立连接
* client地址存放在第二个參数中
* 以后用新的socket描写叙述符进行读写操作
*/
iAddrLen = sizeof(struct sockaddr);
iNewSocketSever = accept(iSocketSever, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iNewSocketSever != -1)
{
/* 成功建立连接 */
printf("Get connect from IP:%s, port:%d\n", inet_ntoa(tSocketClientAddr.sin_addr), ntohs(tSocketClientAddr.sin_port)); /* net to ascII */
/* fork系统调用创建子进程 */
if (fork() == 0)
{
/* 子进程源代码 */
while (1)
{
/* recv函数接收client数据并显示,假设flags为0,则和read、write一样的操作 */
iRecvLen = recv(iNewSocketSever, ucRecvBuf, 999, 0); /* flags普通情况下置为0 */
if (iRecvLen <= 0)
{
/* recv出错(-1)或连接关闭(0) */
close(iNewSocketSever);
printf("Disconnected!\n");
return -1;
}
else
{
ucRecvBuf[iRecvLen] = '\0';
printf("The recv msg is : %s\n", ucRecvBuf);
}
}
}
else
{
/* 父进程代码 */
close(iNewSocketSever); /* 父进程关闭已连接描写叙述符尤为重要 */
}
}
}
close(iSocketSever);
return 0;
}

注意,假设使用了SOCK_STREAM套接字。就不能保证一次可以接受到整个数据。

普通情况是要在循环中重复读。直到返回0.


UDPclient:
/* 基于UDP协议的socket编程client代码 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* client:
* socket-->connect-->send/recv
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iRet;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
int iSendLen;
unsigned char ucSendBuf[1000];
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
/* 1. 建立socket */
iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
/* 2. 不会建立真正的连接,仅仅是让socket描写叙述符中带有服务器地址,
* 假设不使用connect函数,则应该使用sento函数发送数据
*/
tSocketServerAddr.sin_family = AF_INET; /* recommend */
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net short */
if (inet_aton(argv[1], &tSocketServerAddr.sin_addr) == 0) /* 字符串转服务器IP */
{
printf("Invalid server IP\n");
return -1;
}
bzero(tSocketServerAddr.sin_zero, 8);
/* UDP中不须要建立连接,但这里使用connect是能够将地址绑定到iSocketClient上,send函数中就不须要地址信息了 */
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (iRet == -1)
{
printf("connect error\n");
return -1;
}
while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
/* send函数发送数据 */
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if (iSendLen == -1)
{
/* 函数出错 */
close(iSocketClient);
return -1;
}
}
}
return 0;
}

UDP服务端:
/* 基于UDP协议的socket编程server端代码 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
/* server:
* socket-->bind-->sendto/recvfrom
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iRet;
int iAddrLen;
int iSocketSever;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRecvLen;
unsigned char ucRecvBuf[1000];
/* 1. 建立socket
* AF_INET: IPv4 Internet protocols
* SOCK_DGRAM: UDP协议
* 0: 通常赋值
*/
iSocketSever = socket(AF_INET, SOCK_DGRAM, 0);
if (iSocketSever == -1)
{
printf("socket error\n");
return -1;
}
/* 2. 配置socket
* 设置监听本机的哪个IP,端口
*/
tSocketServerAddr.sin_family = AF_INET; /* recommend */
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net short */
tSocketServerAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* 填入本机全部IP */
bzero(tSocketServerAddr.sin_zero, 8);
iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (iRet == -1)
{
printf("bind error\n");
return -1;
}
while (1)
{
/* 接收client数据,地址信息放在tSocketClientAddr结构体中 */
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketSever, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get msg from %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
}
close(iSocketSever);
return 0;
}

UDPclient(无connect函数):
/* 基于UDP协议的socket编程client代码 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* client:
* socket-->sendto/recvfrom
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iRet;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
int iSendLen;
unsigned char ucSendBuf[1000];
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
/* 1. 建立socket */
iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
/* 2. 设置服务器地址 */
tSocketServerAddr.sin_family = htonl(AF_INET); /* recommend */
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net short */
if (inet_aton(argv[1], &tSocketServerAddr.sin_addr) == 0) /* 字符串转服务器IP */
{
printf("Invalid server IP\n");
return -1;
}
bzero(tSocketServerAddr.sin_zero, 8);
while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
/* sendto函数发送数据 */
iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
(const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (iSendLen == -1)
{
/* 函数出错 */
close(iSocketClient);
return -1;
}
}
}
return 0;
}

UDP服务端(无connect函数):
/* 基于UDP协议的socket编程server端代码 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
/* server:
* socket-->bind-->sendto/recvfrom
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iRet;
int iAddrLen;
int iSocketSever;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRecvLen;
unsigned char ucRecvBuf[1000];
/* 1. 建立socket
* AF_INET: IPv4 Internet protocols
* SOCK_DGRAM: UDP协议
* 0: 通常赋值
*/
iSocketSever = socket(AF_INET, SOCK_DGRAM, 0);
if (iSocketSever == -1)
{
printf("socket error\n");
return -1;
}
/* 2. 配置socket
* 设置监听本机的哪个IP,端口
*/
tSocketServerAddr.sin_family = AF_INET; /* recommend */
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net short */
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; /* 填入本机全部IP */
bzero(tSocketServerAddr.sin_zero, 8);
iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (iRet == -1)
{
printf("bind error\n");
return -1;
}
while (1)
{
/* 接收client数据。地址信息放在tSocketClientAddr结构体中 */
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketSever, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get msg from %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
}
close(iSocketSever);
return 0;
}
參考:
《unix环境高级编程》第16章。

【Linux编程】socket编程的更多相关文章

  1. LInux下socket编程学习笔记

    1.socket套接字: socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模 ...

  2. Linux下Socket编程的端口问题( Bind error: Address already in use )

    Linux下Socket编程的端口问题( Bind error: Address already in use ) 在进行linux网络编程时,每次修改了源代码并再次编译运行时,常遇到下面的地使用错误 ...

  3. linux下socket编程实例

    linux下socket编程实例一.基本socket函数Linux系统是通过提供套接字(socket)来进行网络编程的.网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.s ...

  4. Linux的SOCKET编程详解(转)

    Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系 ...

  5. 【ARM-Linux开发】Linux的SOCKET编程详解

    Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系 ...

  6. Linux下socket编程基本知识

    本文档主要讲解了Linux下socket编程的一些基本知识,主要包括套接字和字节序的概念,以及一些常用的结构体和函数. 本文是在网易云课堂学习过程中的记录,这个老师讲得很不错,推荐大家围观. Linu ...

  7. linux下socket编程

    相关结构 //下边这两个结构定义在<sys/types.h>里 //一般的地址结构,只能用于覆盖(把其他地址转换为此类型),且只能引用该地址的sa_family字段 struct sock ...

  8. [转] - linux下socket编程实例

    一.基本socket函数Linux系统是通过提供套接字(socket)来进行网络编程的.网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.socket也有一个类似于打开文件 ...

  9. Linux的SOCKET编程详解

    1. 网络中进程之间如何通信 进 程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系统为进程通信提供了相应设施,如 U ...

  10. linux多线程socket编程一些心得

    http://hi.baidu.com/netpet/blog/item/2cc79216d9012b54f2de32b9.html 前段时间将新的web模型办到linux上来,用epoll代替了IO ...

随机推荐

  1. oracle数据库安装教程以及问题和解决方法

    一,oracle数据库的下载和安装 1,在oracle官网上下载oracle安装包,运行setup.exe. 2,点击取消“我希望同构My Oracle Support接受安全更新”,以防后患. 3, ...

  2. (七)u-boot2013.01.01 for s5pv210:《u-boot启动流程》

    转载请注明地址:http://blog.csdn.net/zsy2020314/article/details/9824035 1.关于启动流程 1.1 启动阶段分为3个,bl0,bl1,bl2.下面 ...

  3. Python 绘图与可视化 matplotlib 填充fill和fill_between

    参考链接:https://blog.csdn.net/You_are_my_dream/article/details/53457960 fill()填充函数曲线与坐标轴之间的区域: x = np.l ...

  4. plsql 中出现 Dynamic Performance Tables not accessible 问题解决

    产生该提示原因: plsql dev在用户运行过程中,要收集用户统计信息,但是由于你现在登录的用户没有访问v$session,v$sesstat and v$statname视图的权限, 所以不能收集 ...

  5. JS中的五种去重方法

    JS中的五种去重方法 第一种方法: 第二种方法:  第三种方法: 第四种方法: 第五种方法:优化遍历数组法 思路:获取没重复的最右一值放入新数组 * 方法的实现代码相当酷炫,* 实现思路:获取没重复的 ...

  6. js实现点击复制网页内容(基于clipboard.js)

    浏览网页过程中会遇到点击复制链接地址的情况,下面就介绍一种实现方法,该方法是基于clipboard.js: 官网地址:https://clipboardjs.com/: clipboard.js使用比 ...

  7. RabbitMQ学习总结(6)——消息的路由分发机制详解

    一.Routing(路由) (using the Java client) 在前面的学习中,构建了一个简单的日志记录系统,能够广播所有的日志给多个接收者,在该部分学习中,将添加一个新的特点,就是可以只 ...

  8. jvm 虚拟机参数_堆内存分配

    1.参数 -XX:+PrintGC 只要遇到 GC 就会打印日志 -XX:+UseSerialGC 配置串行回收器 -XX:+PrintGCDetails 查看详细信息,包括各个区的情况 -XX:+P ...

  9. [terry笔记]python FTP

    如下是作业,用python做一个ftp,主要利用socket. server端在linux下运行,在client端可以执行shell命令(静态的) 在client端输入get xxx,即可下载. 在c ...

  10. linux内核(三)文件系统

    1.为什么需要根文件系统 (1)init进程的应用程序在根文件系统上(2)根文件系统提供了根目录/(3)内核启动后的应用层配置(etc目录)在根文件系统上.几乎可以认为:发行版=内核+rootfs(4 ...