Linux下的Socket编程大体上包括Tcp Socket、Udp Socket即Raw Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较多的,而Raw Socket则用得相对较少,不在本文介绍范围之列。

TCP Socket

基于TCP协议的客户端/服务器程序的一般流程一般如下:

它基本上可以分为三个部分:

一、建立连接:

  • 服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态
  • 客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答
  • 服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。

二、传输数据:

建立连接后,TCP协议提供全双工的通信管道,服务器端和客户端根据协议可以通过read和write的反复调用实现数据的传输

三、关闭连接:

当数据传输已经完成后,服务器和客户端可以调用Close关闭连接,一端关闭连接后,另一端read函数则会返回0,可以根据这个特征来感应另一端的退出。

下面就以一个简单的EchoServer演示一下如何创建服务器端和客户端代码,其中和socket相关api都会高亮显示。

服务器端示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MAXLINE 80
#define SERV_PORT 8000 int main(void)
{
char buf[MAXLINE]; int listenfd = 0;
listenfd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20); printf("Accepting connections ...\n");
while (1)
{
sockaddr_in cliaddr = {0};
socklen_t cliaddr_len = sizeof(cliaddr);
int connfd = accept(listenfd, (sockaddr *)&cliaddr, &cliaddr_len); char str[INET_ADDRSTRLEN];
printf("connected from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); while(true)
{
int count = read(connfd, buf, MAXLINE);
if (count == 0)
break; write(connfd, buf, count);
} close(connfd);
printf("closed from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
}
}

PS:这里需要注意的一下的是sock函数的第二个参数SOCK_STREAM,它表示是一个TCP连接,后面我们会介绍通过传入SOCK_DGRAM打开udp连接。

服务器端主体流程就是一个死循环,它接受一个socket连接,然后将其原封不动的返回给客户端,待客户端退出后,关闭socket连接,再次接受下一个socket连接。

客户端代码如下:

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h> #define MAXLINE 80
#define SERV_PORT 8000
#define MESSAGE "hello world" int main(int argc, char *argv[])
{
char buf[MAXLINE]; int sockfd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT); if (0 != connect(sockfd, (sockaddr *)&servaddr, sizeof(servaddr)))
{
printf("connected failed");
return 1;
} write(sockfd, MESSAGE, sizeof(MESSAGE));
int count = read(sockfd, buf, MAXLINE); printf("Response from server: %s\n",buf); close(sockfd);
return 0;
}

UDP Socket

典型的UDP客户端/服务器通讯过程如下图所示:

由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现,可能反而会需要更多代码。

典型的示例如下:

/* server.cpp */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MAXLINE 80
#define SERV_PORT 8000 int main(void)
{
char buf[MAXLINE];
char str[INET_ADDRSTRLEN]; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); bind(sockfd, (sockaddr *)&servaddr, sizeof(servaddr)); printf("Accepting connections ...\n");
while (1)
{
sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr); int count = recvfrom(sockfd, buf, MAXLINE, 0, (sockaddr *)&cliaddr, &cliaddr_len);
if (count < 0)
{
printf("recvfrom error");
continue;
} printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); sendto(sockfd, buf, count, 0, (sockaddr *)&cliaddr, sizeof(cliaddr));
}
} /* client.cpp */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MAXLINE 80
#define SERV_PORT 8000 int main(int argc, char *argv[])
{
char buf[MAXLINE];
char str[INET_ADDRSTRLEN]; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT); while (fgets(buf, MAXLINE, stdin) != NULL)
{
int count = sendto(sockfd, buf, strlen(buf), 0, (sockaddr *)&servaddr, sizeof(servaddr));
if (count == -1)
{
printf("sendto error");
return 0;
} count = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);
if (count == -1)
{
printf("recvfrom error");
return 0;
} write(STDOUT_FILENO, buf, count);
} close(sockfd);
return 0;
}

最新文章

  1. nth-of-type在选择class的时候需要注意的一个小问题
  2. 一个C#语法高亮插件
  3. OLE/COM 对象查看器 &amp; OLE常用术语
  4. UIStoryBoard 中修改控件borderColor
  5. A letter to a good guy in USA
  6. 学习笔记之Java程序设计实用教程
  7. 解决 Zabbix agent on [HOSTNAME] is unreachable for 5 minutes
  8. node express
  9. UFLDL实验报告1: Softmax Regression
  10. java算法 蓝桥杯(题+答案) 压缩变换
  11. [笔记]RankSVM 和 IR SVM
  12. 使用Linux的alternatives命令替换选择软件的版本
  13. 二维数组的最大子数组和 时间复杂度:O(n的四次方)
  14. error: can&#39;t copy &#39;docx\templates\default-docx-template&#39;: doesn&#39;t exist or not a regular file --------------- Failed building wheel for python-docx; python-docx的安装使用;python操作word
  15. 那一夜,风雪交加,我在搞jquery------专题之jquery选择器
  16. Eclipse纯净版安装web插件
  17. pandas 拆分groupby 应用某个函数apply 和聚合结果aggregate
  18. Delphi中封装ADO之我重学习记录
  19. 更改centos源为aliyun
  20. java 多线程 25 :线程和线程组的异常处理

热门文章

  1. EndPoint详解
  2. React+BootStrap+ASP.NET MVC实现自适应和组件的复用
  3. python实现curl功能
  4. Android 5.0/5.1开发问题专贴
  5. c++ string 和wstring 之间的互相转换函数
  6. maven nexus-staging-maven-plugin exception-connect timed out
  7. Think in java 4th读书笔记__last update20151130
  8. (ETW) Event Trace for Windows 提高 (含pdf下载)
  9. 高级屏幕空间反射: Screen Space Reflection (SSR)
  10. CDN技术分享