socket编程教程_C语言中文网

代码参考socket

sokect原理参考

服务器: 它是被动的,它只能等待别人跟它建立连接,自己不会去主动连接;

原理:使用SOCKET实现TCP/IP协议的通讯

参考资料_什么是原端口和目的端口

基本TCP套接字编程讲解_流程图

图解TCP-IP协议

Socket详解

[MAKEWORD(2,2)使用][]

[[MAKEWORD(2,2)使用]: http://blog.sina.com.cn/s/blog_a74f39a201010sb3.html

什么叫“低八位、高八位;低四位、高四位”

c++中冒号(:)和双冒号(::)的用法

Windows Socket和Linux Socket编程的区别

C++使用thread类多线程编程


基础概念

  1. TCP/IP协议族

    英语:TCP/IP Protocol Suite,或TCP/IP Protocols),简称TCP/IP
    因为该协议家族的两个核心协议:TCP(传输控制协议)和IP(网际协议),为该家族中最早通过的标准[3]。由于在网络通讯协议普遍采用分层的结构,当多个层次的协议共同工作时,类似计算机科学中的堆栈,因此又被称为TCP/IP协议栈(英语:TCP/IP Protocol Stack)

  2. 端口
    端口就是特定的程序或者软件也可以理解为特定软件或程序的接口,

  3. 原端口/目的端口
    源端口就是本机程序用来发送数据的端口,目的端口就是对方主机用哪个端口接收;

    封目的端口自己就出不去,封源端口别人就进不来。

数据发送时计算机会在数据段上添上本机端口号(源端口号)和目的主机接收数据的端口号(目的端口号)
数据包通过网络设备走的时候,第一个检查的就是你这个数据包的目的地址在什么地方。

  1. TCP通信3个步骤:
    1)建立TCP连接很简单,通过三次握手便可建立连接。
    2)建立好连接后,开始传输数据。TCP数据传输牵涉到的概念很多:超时重传、快速重传、流量控制、拥塞控制等等。
    3)断开连接的过程也很简单,通过四次握手完成断开连接的过程。

  2. TCP三次握手建立连接
    第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

  3. ACK
    用于确认资料有无正确的传输到接收端,在ASCII中编号为6

  4. 网络中进程之间如何通信
    网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

  5. 侦听端口
    服务器倾听端口 ,是服务器监听客户端用的 ,相当于”地址” ,是服务器与客户端间的纽带

TCP/IP首先要有一个服务端,它是被建立连接的;


  • WSAStartup

WSAStartup就是为了向操作系统说明,我们要用哪个库文件,让该库文件与当前的应用程序绑定,从而就可以调用该版本的socket的各种函数了。

WSAStartup解释1

WSAStartup() - 使用方法
当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。

  • socket

Windows 下也使用 socket() 函数来创建套接字,原型为:

1
SOCKET socket(int af, int type, int protocol);

除了返回值类型不同,其他都是相同的。Windows 不把套接字作为普通文件对待,而是返回 SOCKET 类型的句柄。请看下面的例子:

1
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
  1. af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。

  2. type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM,在《socket是什么意思》一节中已经进行了介绍。

    1. SOCK_STREAM 表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。
      1. SOCK_DGRAM 表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
  3. protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

  • bind 函数

调用函数 socket 创建套接字描述符时,该套接字描述符是存储在它的协议族空间中,没有具体的地址,要使它与一个地址相关联,可以调用函数bind 使其与地址绑定。客户端的套接字关联的地址一般可由系统默认分配,因此不需要指定具体的地址。若要为服务器端套接字绑定地址,可以通过调用函数 bind 将套接字绑定到一个地址。下面是该函数的描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 套接字的基本操作 */   
/*
* 函数功能:将协议地址绑定到一个套接字;其中协议地址包含IP地址和端口号;
* 返回值:若成功则返回0,若出错则返回-1;
* 函数原型:
*/
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
* 说明:
* sockfd 为套接字描述符;
* addr是一个指向特定协议地址结构的指针;
* addrlen是地址结构的长度;
*/
  • ``SOCKADDR_IN`` 结构体
    

sockaddr_in结构体简介

代码解析

1
#pragma comment(lib, "ws2_32.lib") 

静态加入一个lib文件

ws2_32.lib文件,提供了对以下网络相关API的支持,若使用其中的API,则应该将ws2_32.lib加入工程(否则需要动态载入ws2_32.dll)。
这句话一般出现在网络编程中,需要使用网络API函数的时候,就必须使用这条语句加载ws2_32.lib库或者动态载入ws2_32.dll

非阻塞套接字

参考_Windows Socket 非阻塞模式开发

socket同步/异步通信 []

重点参考_TCP Server处理多Client请求的方法—非阻塞accept与select

重点参考_linux下TCP连接的client和server

可参考代码_C++多线程socket系统新编

多线程

参考代码_C++多线程socket系统新编

Socket编程模型之简单选择模型

使用Cout输出String和CString对象

C++基础::为什么不能cout一个string?

C++中的C_str()函数用法

fork()

对于主进程 fork()返回新建的子进程ID, 子进程fork()返回0

fork()函数详解

问题

  • 提示:blind fail

尝试解决:

使用

1
SO_REUSEADDR
  • socket客户端使用recv从服务器`接收数据:有“烫烫”乱码

结论:
TCP中send和recv函数针对的是字符(无边界), 而不是字符串(以’\0’作为边界)。 换句话说, tcp的send和recv函数对所传的内容不敏感, 它关注的是字符, 仅此而已。
recv要做数组初始化,否则会出现烫烫的乱码

问题代码

1
2
3
4
  recv(sHost, bufRecv,5 , 0);     // 接收服务器端的数据, 只接收5个字符  
cout << endl <<"从服务器接收数据:" << bufRecv;
}
//退出

方法1.

解决后的代码
手动在recv接受数组添加0作为结束

1
2
3
4
5
	auto len = recv(sHost, bufRecv, BUF_SIZE, 0);     // 接收服务器端的数据, 只接收5个字符			// client4:  读
bufRecv[len] = 0;
cout << endl << "从服务器`接收数据:" << bufRecv;
}
//退出

方法2.

在recv前做数组初始化

1
2
char bufRecv[100] = { 0 };
recv(sHost, bufRecv, 100, 0);

方法3.
在recv前做清零操作

1
2
ZeroMemory(bufRecv, BUF_SIZE);  //清0   /*或者这样:    memset(bufRecv, 0, BUF_SIZE);*/
recv(sHost, bufRecv, BUF_SIZE, 0);
  1. 是不是说char数组没有结尾\0输出会出现乱码?

  2. server端,send数组明明有\0但是client端的rev却没有接收到\0

需要了解_c语言——‘\0’,’0’,”0” ,0之间的区别

需要了解_字符和字符字面值

CSDN类似问题

District 9(2009

  1. typename
    “typename”是一个C++程序设计语言中的关键字。当用于泛型编程时是另一术语”class”的同义词。[1]这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)是类型名,而非变量名。以下是对于泛型编程中typename两种迥然不同的用法的解释。

char 数组长度不能存储’\0’,导致 后面的字符贴上来了

#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
using namespace  std;


typedef unsigned char U8;

typedef struct stInitMode {
    char chModeStartTime[15];	    // IN,模式发生时间(YYYYMMDDhh24mmss);
    U8 ucModeID;					// IN,模式ID (参见第4分册“3.2.11 降级运营模式ID”)
}_stInitMode;


struct stInitReaderInfo_CD
{
    char chLineNumber[3];				// IN,线路编码
    char chStationNumber[3];			// IN,车站编码
    char chEquipType[3];				// IN,设备类型
    char chEquipNumber[4];				// IN,3bytes设备编号
    U8 EquipWorkMode;					// IN,设备工作模式(读写器工作模式:0x00——使用指令中的工作模式;0x10——ES;0x20——非付费区BOM;0x21——付费区BOM;0x22——TVM;0x23——TCM;0x24——PCA;0x40——进站AGM;0x41——出站AGM)
    U8 DownWorkModenum;					// IN,模式数量
    char chRCodeBaudrate[12];       //  IN,关联扫码枪通讯波特率
    _stInitMode Mode[16];
    //	stInitMode_CD  Mode[16];

    char chRCodeCOM[10];            //  IN,关联扫码枪串口号
    char chModeStartTime[15];			// IN,模式发生时间(YYYYMMDDhh24mmss);
    char chRCodeServerAddr[256];    //  IN, 支付平台接入地址,例如http://182.140.238.35:61082/
    U8 ucModeID;						// IN,模式ID (参见第4分册“3.2.11 降级运营模式ID”)
    bool Mark_IsUseQRcode;           //take:add是否启用二维码  在读写器里传参标志需要
};

typedef struct stInitReaderInfo {
    char chLineNumber[3];			// IN,线路编码
    char chStationNumber[3];		// IN,车站编码
    char chEquipmentType[3];		// IN,设备类型
    char chEquipmentNumber[4];		// IN,3bytes设备编号
    U8 ucEquipWorkMode;				// IN,设备工作模式(读写器工作模式:0x00——使用指令中的工作模式;0x10——ES;0x20——非付费区BOM;0x21——付费区BOM;0x22——TVM;0x23——TCM;0x24——PCA;0x40——进站AGM;0x41——出站AGM)
    U8 ucDownWorkModenum;			// IN,模式数量
    _stInitMode Mode[16];
    //char chEquipmentID[17];			// OUT,16位PSAM卡ID
} _stInitReaderInfo;


typedef struct _stRCodeInfo {
    char chRCodeCOM[10];            //  IN,关联扫码枪串口号
    char chRCodeBaudrate[12];       //  IN,关联扫码枪通讯波特率
    char chRCodeServerAddr[256];    //  IN, 支付平台接入地址,例如http://182.140.238.35:61082/
} _stRCodeInfo;




void startReaderAFC(void* i_StartAFCData)
{


    /****>>>>>>>>>>>>>>>>>>take:第一个参数结构体***>>>>>>>>>>>>>>>>>>>>>>>>>******************/

    _stInitReaderInfo take_stInitReaderInfo;
    strcpy(take_stInitReaderInfo.chLineNumber, ((stInitReaderInfo_CD*)i_StartAFCData)->chLineNumber);
    strcpy(take_stInitReaderInfo.chStationNumber, ((stInitReaderInfo_CD*)i_StartAFCData)->chStationNumber);
    strcpy(take_stInitReaderInfo.chEquipmentType, ((stInitReaderInfo_CD*)i_StartAFCData)->chEquipType);
    strcpy(take_stInitReaderInfo.chEquipmentNumber, ((stInitReaderInfo_CD*)i_StartAFCData)->chEquipNumber);
    take_stInitReaderInfo.ucEquipWorkMode = ((stInitReaderInfo_CD*)i_StartAFCData)->EquipWorkMode;
    take_stInitReaderInfo.ucDownWorkModenum = ((stInitReaderInfo_CD*)i_StartAFCData)->DownWorkModenum;
    //take_stInitReaderInfo.Mode = ((stInitReaderInfo_CD*)i_StartAFCData)->Mode;
    memcpy(take_stInitReaderInfo.Mode, ((stInitReaderInfo_CD*)i_StartAFCData)->Mode, sizeof(take_stInitReaderInfo.Mode));//take:这样可以吗?

    /***<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<take:第一个参数结构体**<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<******/


    /*>>>>>>>>>>>>>>>>>>>>>>>>>>>take:第二个参数*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
    _stRCodeInfo *ptr_stRCodeInfo = (_stRCodeInfo*)malloc(sizeof(_stRCodeInfo));
    
    memset(ptr_stRCodeInfo->chRCodeBaudrate, '\0', sizeof(ptr_stRCodeInfo->chRCodeBaudrate));
    memset(ptr_stRCodeInfo->chRCodeCOM, '\0', sizeof(ptr_stRCodeInfo->chRCodeCOM));
    memset(ptr_stRCodeInfo->chRCodeServerAddr, '\0', sizeof(ptr_stRCodeInfo->chRCodeServerAddr));

    cout << "(stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM):" << ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM << endl;
    strcpy(ptr_stRCodeInfo->chRCodeCOM, ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM);

    cout << ptr_stRCodeInfo->chRCodeCOM << " line:" << __LINE__ << endl;   //只有这里是正确的

    strcpy(ptr_stRCodeInfo->chRCodeBaudrate, ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeBaudrate);
    cout << ptr_stRCodeInfo->chRCodeCOM <<" line:"<<__LINE__<< endl;
    strcpy(ptr_stRCodeInfo->chRCodeServerAddr, ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeServerAddr);

    cout << ptr_stRCodeInfo->chRCodeCOM << " line:" << __LINE__ << endl;
    //cout << "(stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM):" << ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM << endl;
    /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*******************************************************<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
//	cout << ((test*)i_StartAFCData)->chEquipNumber << endl;



//	cout<<((stInitReaderInfo_CD*)i_StartAFCData)->chEquipmentNumber;
}

int main()
{
    stInitReaderInfo_CD temp[2];
 

//	strcpy(temp[0].chEquipNumber_, "aa");
    temp[0].EquipWorkMode = 34;
    
     strcpy(temp[0].Mode[0].chModeStartTime, "201804-=18");
    strcpy( temp[0].chLineNumber, "nu");

    strcpy(temp[0].chStationNumber,"st");
     strcpy(temp[0].chEquipType,"tp");
     strcpy(temp[0].chEquipNumber, "chn");
     sprintf(temp[0].chRCodeBaudrate, "%d", 115200);
     strcpy(temp[0].chRCodeCOM, "/dev/ttyS8");
     strcpy(temp[1].chRCodeCOM, "/dev/ttyS9");

     temp[1] = { 0 };
     
  startReaderAFC(&temp);

 
 

}

Fork me on GitHub