热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

用C语言进行最基本的socket编程

这篇文章主要介绍了C语言下socket编程的基本知识讲解,包括最基本的客户端发送及服务器端接受数据的实现,需要的朋友可以参考下

什么是socket
  你经常听到人们谈论着 “socket”,或许你还不知道它的确切含义。现在让我告诉你:它是使用 标准Unix 文件描述符 (file descriptor) 和其它程序通讯的方式。什么?你也许听到一些Unix高手(hacker)这样说过:“呀,Unix中的一切就是文件!”那个家伙也许正在说到一个事实:Unix 程序在执行任何形式的 I/O 的时候,程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。但是(注意后面的话),这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其它的东西。Unix 中所有的东西就是文件!所以,你想和Internet上别的程序通讯的时候,你将要使用到文件描述符。你必须理解刚才的话。现在你脑海中或许冒出这样的念头:“那么我从哪里得到网络通讯的文件描述符呢?”,这个问题无论如何我都要回答:你利用系统调用 socket(),它返回套接字描述符 (socket descriptor),然后你再通过它来进行send() 和 recv()调用。“但是...”,你可能有很大的疑惑,“如果它是个文件描述符,那么为什 么不用一般调用read()和write()来进行套接字通讯?”简单的答案是:“你可以使用!”。详细的答案是:“你可以,但是使用send()和recv()让你更好的控制数据传输。”存在这样一个情况:在我们的世界上,有很多种套接字。有DARPA Internet 地址 (Internet 套接字),本地节点的路径名 (Unix套接字),CCITT X.25地址 (你可以将X.25 套接字完全忽略)。也许在你的Unix 机器上还有其它的。我们在这里只讲第一种:Internet 套接字。
Internet 套接字的两种类型 :
  什么意思?有两种类型的Internet 套接字?是的。不,我在撒谎。其实还有很多,但是我可不想吓着你。我们这里只讲两种。除了这些, 我打算另外介绍的 "Raw Sockets" 也是非常强大的,很值得查阅。
那么这两种类型是什么呢?一种是"Stream Sockets"(流格式),另外一种是"Datagram Sockets"(数据包格式)。我们以后谈到它们的时候也会用到"SOCK_STREAM" 和 "SOCK_DGRAM"。数据报套接字有时也叫“无连接套接字”(如果你确实要连接的时候可以用connect()。) 流式套接字是可靠的双向通讯的数据流。如果你向套接字按顺序输出“1,2”,那么它们将按顺序“1,2”到达另一边。它们是无错误的传递的,有自己的错误控制,在此不讨论。
    有什么在使用流式套接字?你可能听说过 telnet,不是吗?它就使用流式套接字。你需要你所输入的字符按顺序到达,不是吗?同样,WWW浏览器使用的 HTTP 协议也使用它们来下载页面。实际上,当你通过端口80 telnet 到一个 WWW 站点,然后输入 “GET pagename” 的时候,你也可以得到 HTML 的内容。为什么流式套接字可以达到高质量的数据传输?这是因为它使用了“传输控制协议 (The Transmission Control Protocol)”,也叫 “TCP” (请参考 RFC-793 获得详细资料。)TCP 控制你的数据按顺序到达并且没有错
误。你也许听到 “TCP” 是因为听到过 “TCP/IP”。这里的 IP 是指“Internet 协议”(请参考 RFC-791。) IP 只是处理Internet 路由而已。
    那么数据报套接字呢?为什么它叫无连接呢?为什么它是不可靠的呢?有这样的一些事实:如果你发送一个数据报,它可能会到达,它可能次序颠倒了。如果它到达,那么在这个包的内部是无错误的。数据报也使用 IP 作路由,但是它不使用 TCP。它使用“用户数据报协议 (User Datagram Protocol)”,也叫 “UDP” (请参考 RFC-768。)
    为什么它们是无连接的呢?主要是因为它并不象流式套接字那样维持一个连接。你只要建立一个包,构造一个有目标信息的IP 头,然后发出去。无需连接。它们通常使用于传输包-包信息。简单的应用程序有:tftp, bootp等等。
    你也许会想:“假如数据丢失了这些程序如何正常工作?”我的朋友,每个程序在 UDP 上有自己的协议。例如,tftp 协议每发出的一个被接受到包,收到者必须发回一个包来说“我收到了!” (一个“命令正确应答”也叫“ACK” 包)。如果在一定时间内(例如5秒),发送方没有收到应答,它将重新发送,直到得到 ACK。这一ACK过程在实现SOCK_DGRAM 应用程序的时候非常重要。

简单的发送和接收实现

服务器端接收代码:

#include 
#pragma comment(lib,"Ws2_32.lib")
#include 
#include 

void main()
{
WSAData wsd;
WSAStartup(MAKEWORD(2,0),&wsd);

SOCKET s =NULL;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockaddr_in ch;
memset(&ch,0,sizeof(ch));
ch.sin_family=AF_INET;
ch.sin_addr.s_addr=INADDR_ANY;
ch.sin_port=htons(1041);
int b=bind(s,(struct sockaddr *) &ch,sizeof(ch));
#define QUEUE_SIZE 5
int l=listen(s,QUEUE_SIZE);
printf("正在监听本机的1041端口!\n");
SOCKET sc=accept(s,0,0);
printf("客户端已经连接到本机的1041端口!\n");
#define BUF_SIZE 4096
int receByt=0;
while(1)
{
char buf[BUF_SIZE];
receByt=recv(sc,buf,BUF_SIZE,0);
buf[receByt]='\0';
if(receByt>0)
{
printf("接收的消息是:%s\n",buf);
}
else
{
printf("接收消息结束!");
break;
}

}
int ic=closesocket(sc);
int is=closesocket(s);

}

客户端发送的代码:

#include 
#pragma comment(lib,"Ws2_32.lib")
#include 
#include 
#include 

void main()
{
WSAData wsd;
WSAStartup(MAKEWORD(2,0),&wsd);

SOCKET s =NULL;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockaddr_in ch;
memset(&ch,0,sizeof(ch));
ch.sin_family=AF_INET;
ch.sin_addr.s_addr=inet_addr("127.0.0.1");
ch.sin_port=htons(1041);

int c=connect(s,(struct sockaddr *) &ch,sizeof(ch));
printf("已经连接到服务器的1041端口!现在可以向服务器发送消息了!\n");
#define BUF_SIZE 4096
char info[1024],buf[BUF_SIZE];

while(1)
{
gets(info);
if(info[0]=='\0')
break;
strcpy(buf,info);
int nsend=send(s,buf,strlen(buf),0);

}
int ic=closesocket(s);
}

程序代码经过了优化,并且整合多线程,把接收和发送放到同一个文件中,使用参数模式调用发送和接收模块。增加了创建SOCKET的创建的时候s句柄(或对象)判断返回值是否为INVALID_SOCKET,以及socket的bind操作的返回值是否为SOCKET_ERROR,其他socket的操作应该也判断SOCKET_ERROR,以保证程序的稳定性,这里只是测试代码就不去写这么多了,剩下的就由你个人发挥。

#include 
#pragma comment(lib,"Ws2_32.lib")
#include 
#include 
#include 
#include 


void Receive();
void Send();
void creatThread();

SOCKET s =NULL;
pthread_t t[1000];
int threadCount=0;

void main(int argc,char* argv[])
{
  printf("本程序制作人学号:713901040041\n");
  printf("程序说明:服务器端和客户端为同一个程序,请使用不同的参数运行.\n");
  printf("接收程序请使用 r参数;发送程序请使用 s参数。\n");
  //printf("len : %d\n", argc);
  //printf("count %d\n",argc);
  //printf("value: %s\n",argv[1]);
  //printf("%d",argv[1][0]=='r');

  if(argc<=1)
  {
    printf("please input program arguments ...\n");
    exit(0);
  }
  if(argc>1 && argv[1][0]=='r')
  {
    printf("run receive ...\n");
    Receive();
  }
  if(argc>1 && argv[1][0]=='s')
  {
    printf("run send ...\n");
    Send();
  }
}


void* receiveWork(void * args)
{
  SOCKET sc=accept(s,0,0);
  if(sc==INVALID_SOCKET)
  {
    printf("sc Error");
  }
  creatThread();

  printf("----------客户端已经连接到本机的%d线程连接!\n",threadCount-2);
#define BUF_SIZE 4096
  int receByt=0;
  while(1)
  {
    char buf[BUF_SIZE];
    receByt=recv(sc,buf,BUF_SIZE,0);
    buf[receByt]='\0';
    if(receByt>0)
    {
      printf("线程接收的消息是:%s\n",buf);
    }
    else
    {
      printf("客户端已退出,");
      break;
    }
      
  }
  int ic=closesocket(sc);
  printf("服务器结束连接!\n");
  return NULL;
}

void creatThread()
{
  pthread_create(&t[threadCount++],NULL,receiveWork,NULL);
}


void Receive()
{
  WSAData wsd;
  WSAStartup(MAKEWORD(2,0),&wsd);  
  s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(s==INVALID_SOCKET)
  {
    printf("socket created Error");
  }
  struct sockaddr_in ch;
  memset(&ch,0,sizeof(ch));
  ch.sin_family=AF_INET;
  ch.sin_addr.s_addr=INADDR_ANY;
  ch.sin_port=htons(1041);
  int b=bind(s,(struct sockaddr *) &ch,sizeof(ch));
  if(b==SOCKET_ERROR)
  {
    printf("bind 失败,出错代码是:%d\n",WSAGetLastError());
    exit(0);
  }
#define QUEUE_SIZE 5
  int l=listen(s,QUEUE_SIZE);
  printf("正在监听本机的1041端口!\n");
  
  creatThread();

  for(int i=0;i<1000;i++)
  {
    pthread_join(t[i],NULL);
  }

int is=closesocket(s);
}



void Send()
{
  WSAData wsd;
  WSAStartup(MAKEWORD(2,0),&wsd);

  SOCKET s =NULL;
  s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(s==INVALID_SOCKET)
  {
    printf("socket created Error");
  }
  struct sockaddr_in ch;
  memset(&ch,0,sizeof(ch));
  ch.sin_family=AF_INET;
  ch.sin_addr.s_addr=inet_addr("127.0.0.1");
  ch.sin_port=htons(1041);

  int c=connect(s,(struct sockaddr *) &ch,sizeof(ch));
  printf("已经连接到服务器的1041端口!现在可以向服务器发送消息了!\n");
#define BUF_SIZE 4096
  char info[1024],buf[BUF_SIZE];

  while(1)
  {
    gets(info);
    if(info[0]=='\0')
      break;
    strcpy(buf,info);
    int nsend=send(s,buf,strlen(buf),0);
  }
  int ic=closesocket(s);
}


推荐阅读
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 访问一个网页的全过程
    准备:DHCPUDPIP和以太网启动主机,用一根以太网电缆连接到学校的以太网交换机,交换机又与学校的路由器相连.学校的这台路由器与一个ISP链接,此ISP(Intern ... [详细]
  • TCP/IP基础知识详解
    本文详细介绍了TCP/IP协议的基本概念,包括网络层次模型、TCP三次握手过程、四次挥手过程以及TCP与UDP的比较。通过这些内容,读者可以更好地理解TCP/IP协议的工作原理。 ... [详细]
  • 随着网络安全威胁的不断演变,电子邮件系统成为攻击者频繁利用的目标。本文详细探讨了电子邮件系统中的常见漏洞及其潜在风险,并提供了专业的防护建议。 ... [详细]
  • 本文介绍如何在现有网络中部署基于Linux系统的透明防火墙(网桥模式),以实现灵活的时间段控制、流量限制等功能。通过详细的步骤和配置说明,确保内部网络的安全性和稳定性。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 自己用过的一些比较有用的css3新属性【HTML】
    web前端|html教程自己用过的一些比较用的css3新属性web前端-html教程css3刚推出不久,虽然大多数的css3属性在很多流行的浏览器中不支持,但我个人觉得还是要尽量开 ... [详细]
  • 深入解析TCP/IP五层协议
    本文详细介绍了TCP/IP五层协议模型,包括物理层、数据链路层、网络层、传输层和应用层。每层的功能及其相互关系将被逐一解释,帮助读者理解互联网通信的原理。此外,还特别讨论了UDP和TCP协议的特点以及三次握手、四次挥手的过程。 ... [详细]
  • NTP服务器配置详解:原理与工作模式
    本文深入探讨了网络时间协议(NTP)的工作原理及其多种工作模式,旨在帮助读者全面理解NTP的配置参数和应用场景。NTP是基于RFC 1305的时间同步标准,广泛应用于分布式系统中,确保设备间时钟的一致性。 ... [详细]
  • 本文深入探讨了MAC地址与IP地址绑定策略在网络安全中的应用及其潜在风险,同时提供了针对该策略的破解方法和相应的防御措施。 ... [详细]
  • NFS(Network File System)即网络文件系统,是一种分布式文件系统协议,主要用于Unix和类Unix系统之间的文件共享。本文详细介绍NFS的配置文件/etc/exports和相关服务配置,帮助读者理解如何在Linux环境中配置NFS客户端。 ... [详细]
  • 三菱PLC SLMP协议报文详解
    本文详细解析了三菱PLC中使用的SLMP协议报文结构,包括其工作原理、通信流程及报文格式,旨在帮助工程师和技术人员更好地理解和运用这一协议。 ... [详细]
  • 使用C# .NET构建UDP点对点聊天应用
    本文详细介绍如何利用C# .NET框架开发一个基于UDP协议的点对点聊天程序,包括客户端与服务器之间的连接建立、数据传输等核心功能。 ... [详细]
  • 本文将详细探讨 Linux 系统中的 netstat 命令,该命令用于查看网络状态和连接情况。通过了解 IP 地址和端口的基本概念,我们将更好地理解如何利用 netstat 命令来监控和管理网络服务。 ... [详细]
  • 解析EasyCVR平台国标GB28181协议下的TCP与UDP模式
    在使用EasyCVR视频融合平台过程中,用户常遇到关于端口设置的问题,尤其是TCP和UDP模式的区别。本文将详细介绍这两种模式在GB28181协议下的具体应用及差异。 ... [详细]
author-avatar
mobiledu2502878013
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有