socket编程网络与本地

socket网络编程

//ARM嵌入linux与PC的通信程序   
int fd = 0;
   int newfd = 0;
   int addr_len = sizeof(struct sockaddr_in);
   int recv_len = 0;
   int q=0;
   int z=0;
   int len;
   //struct array y1,y2;
   char buf1[4096];
   int fw;

   struct sockaddr_in addr;
   struct sockaddr_in serv_addr;


   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;       //pint TCP/IP addr_set;
   addr.sin_port = htons(PORT);     //port number;
   addr.sin_addr.s_addr = htonl(INADDR_ANY); //auto fill IP address;

    /*create UDP socket*/
   fd = socket(AF_INET,SOCK_STREAM,0);
   if(fd < 0)
   {
      perror("socket");
      return -1;
   }
   else
   {
      printf("create socket %d success.\n",fd);
   }


//设置套接字选项避免地址使用错误-----后来添加的 避免 bind address already in use
    int on =1;
    if((setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
    {
         perror("setsocketopt failed");
         return 1;
     }
   /*bind data port*/
   if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
   {
      perror("bind");
      close(fd);
      return -1;
   }
   else
   {
      printf("bind the addr to socket %d success.\n",fd);
   }
   if(listen(fd, 3) < 0)
   {
      perror("listen");
      close(fd);
      return -1;
   }
   else
   {
      printf("listen to the socket %d, port %d success.\n",fd,PORT);
   }

      if((newfd = accept(fd, (void *)&serv_addr, &addr_len)) < 0)
      {
         perror("accept");
         close(fd);
         return -1;
      }
      else
      {
         printf("accept new socket %d from addr:%s port:%d sucess.\n",newfd,inet_ntoa(serv_addr.sin_addr),ntohs(serv_addr.sin_port));

服务端任务

  1. Socket()函数创建服务端套接字,声明套接字的协议族与数据格式
  2. 声明代表客户端的SOCKADDR_IN结构,并填充接收的IP(INADDR_ANY所有),端口号,协议族
  3. Bind()绑定服务端套接字与SOCKADDR_IN结构
  4. Listen()监听
  5. Accept()阻塞进程,知道有客户端连接
  6. Recv或者send接收发送数据
  7. Closesocket()

客户端完成的工作

  1. Socket()函数创建客户端套接字,声明套接字的协议族与数据格式;
  2. 声明代表服务端的SOCKADDR_IN结构,并填充服务端的IP(服务端IP地址),端口号,协议族
  3. Bind()绑定服务端套接字与SOCKADDR_IN结构
  4. connect()连接服务端
  5. Recv或者send接收发送数据
  6. Closesocket()

相关的函数

htol:

Linux系统下:htonl(),htons(), ntohl(), ntohs()的头文件及函数定义:

  #include <arpa/inet.h>

  uint32_t htonl(uint32_t hostlong);

  uint16_t htons(uint16_t hostshort);

  uint32_t ntohl(uint32_t netlong);

  uint16_t ntohs(uint16_t netshort);

扫描二维码关注公众号,回复: 3093446 查看本文章

 

  htonl()--"Host to Network Long int"     32Bytes

  ntohl()--"Network to Host Long int"     32Bytes

  htons()--"Host to Network Short int"   16Bytes

  ntohs()--"Network to Host Short int"   16Bytes

这里涉及到

网络字节序与主机字节序

网络字节序就是常见的TCP/IP的IP地址顺序,

主机字节序是不同指令集的处理器处理内存的方式不用,存储顺序不同,例如

I网络字节序都是大端排序

X86指令集的CPU是小端,大端的排序所以就使用到上述的几个函数进行转换

struct sockaddr结构体

 这个结构体是linux的网络编程接口中用来表示IP地址的标准结构体,bindconnect等函数中都需要这个结构体,这个结构体是兼容IPV4IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in所填充。

通常这两个结构是为了给socket套接字相关的bind,accept等函数当参数,一般可以用sockaddr_in结构声明,然后填充IP,端口号,协议族;

头文件#include <sys/socket.h>

Bind函数

把套接字与一套sockaddr结构(包含了协议族,IP,端口号)绑定

linux 环境下为:

1

2

3

4

5

6

7

8

9

10

11

12

13

#include <sys/types.h>

#include <sys/socket.h>

/****

*  sockfd:标识一未捆绑套接口的描述字。

*  my_addr:赋予套接口的地址。sockaddr结构定义如下:

*  struct sockaddr{

*    u_short sa_family;

*    char sa_data[14];

*  };

*  addrlen:name名字的长度。

*  返回值:成功返回0,失败返回-1.

****/

int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);

参数列表中,sockfd 表示已经建立的socket编号(描述符);

my_addr 是一个指向sockaddr结构体类型的指针;

参数addrlen表示my_addr结构的长度,可以用sizeof操作符获得。

如无错误发生,则bind()返回0。否则的话,将返回-1

Listen()函数

创建套接口,监听申请的连接。

int listen( int sockfd, int backlog);

sockfd:用于标识一个已捆绑未连接套接口的描述字。

backlog:等待连接队列的最大长度。

如无错误发生,listen()返回0。否则的话,返回-1

Accept函数

接收连接请求,通常是服务端listen之后

SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:套接字描述符,该套接口在listen()后监听连接。

addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。

addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。

如果没有错误产生,则accept()返回一个描述所接受包的SOCKET类型的值。否则的话,返回INVALID_SOCKET错误,

Connect()函数

客户端连接制定socket

int connect(SOCKET s, const struct sockaddr * name, int namelen);

参数:

s:标识一个未连接socket

name:指向要连接套接字的sockaddr结构体的指针

namelensockaddr结构体的字节长度

返回值:成功则返回0, 失败返回-1, 错误原因存于errno .

 

Recv用于已经连接的,数据报或者流数据的套接字进行数据接收

int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);

该函数的第一个参数指定接收端套接字描述符;

第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

第三个参数指明buf的长度;

第四个参数一般置0

这里只描述同步Socketrecv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,
如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据
被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直
等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copybuf中(注意协议接收到的数据
可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的
接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recvcopy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0

 

 

send不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。

int send( SOCKET s,      const char FAR *buf,      int len,      int flags );  

该函数的第一个参数指定发送端套接字描述符;

第二个参数指明一个存放应用程序要发送数据的缓冲区;

第三个参数指明实际要发送的数据的字节数;

第四个参数一般置0

如果send函数copy数据成功,
就返回实际copy的字节数,如果sendcopy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR

猜你喜欢

转载自blog.csdn.net/zhangzhi2ma/article/details/82414401