网络编程学习笔记——TCP网络编程


网络编程常用函数

  • socket() 创建套接字
  • bind() 绑定本机地址和端口
  • connect() 建立连接
  • listen() 设置监听端口
  • accept() 接受TCP连接
  • recv(), read(), recvfrom() 数据接收
  • send(), write(), sendto() 数据发送
  • close(), shutdown() 关闭套接字

在这里插入图片描述

1、socket()函数

int socket (int domain, int type, int protocol);

  • domain 是地址族
    • PF_INET IPv4 Internet protocols ip(7) // internet 协议
    • PF_UNIX IPv6 Internet protocols ipv6(7) // unix internal协议
    • PF_NS Local communication unix(7) // Xerox NS协议
    • PF_IMPLINK Low level packet interface packet(7)// Interface Message协议
  • type // 套接字类型
    • SOCK_STREAM // 流式套接字
    • SOCK_DGRAM // 数据报套接字
    • SOCK_RAW //原始套接字
  • protocol 参数通常置为0

地址相关的数据结构

  • 通用地址结构

    • struct sockaddr
      {    
           u_short  sa_family;    // 地址族, AF_xxx
           char  sa_data[14];     // 14字节协议地址
      };
      
  • Internet协议地址结构

    • struct sockaddr_in
      {           
           u_short sin_family;		// 地址族, AF_INET,2 bytes
           u_short sin_port;      	// 端口,2 bytes
           struct in_addr sin_addr;	// IPV4地址,4 bytes 	
           char sin_zero[8];			// 8 bytes unused,作为填充
      }; 
      
  • IPv4地址结构

    • // internet address  
      struct in_addr
      {
          in_addr_t  s_addr;            // u32 network address 
      };
      

返回值

  • RETURN VALUE
           On  success,  a  file  descriptor  for  the new socket is returned.  On
           error, -1 is returned, and errno is set appropriately.
    

成功时返回文件描述符,出错时返回为-1

2、bind()函数

int bind (int sockfd, struct sockaddr* addr, int addrLen);

  • sockfd 由socket() 调用返回

  • addr 是指向 sockaddr_in 结构的指针,包含本机IP 地址和端口号(struct sockaddr的结构体变量的地址)

    • struct sockaddr_in

    • u_short sin_family // protocol family 
      u_short sin_port     // port number 
      struct in_addr  sin_addr  //IP address (32-bits)4字节
      
  • addrLen : sizeof (struct sockaddr_in)(地址长度)

地址结构的一般用法:

  • 定义一个struct sockaddr_in类型的变量并清空

    • struct sockaddr_in myaddr;
      memset(&myaddr, 0, sizeof(myaddr));
      
  • 填充地址信息

    • myaddr.sin_family = PF_INET;
      myaddr.sin_port = htons(8888); 
      myaddr.sin_addr.s_addr = inet_addr(“192.168.1.100”);
      
  • 将该变量强制转换为struct sockaddr类型在函数中使用

    • bind(listenfd, (struct sockaddr*)(&myaddr), sizeof(myaddr));
      

地址转换函数:

  • unsigned long inet_addr(char *address);

    • address是以’\0’结尾的点分IPv4字符串。该函数返回32位的地址。如果字符串包含的不是合法的IP地址,则函数返回-1。例如:

      struct in_addr addr;
      addr.s_addr = inet_addr(" 192.168.1.100 ");
      
  • char* inet_ntoa(struct in_addr address);

    • address是IPv4地址结构,函数返回一指向包含点分IP地址的静态存储区字符指针。如果错误则函数返回NULL

代码举例:

#include "net.h"

int main(void)
{

	int fd = -1;
	struct sockaddr_in sin;
        
	/* 1. 创建socket fd*/
	if( (fd = socket(AF_INET,SOCK_STREAM, 0)) < 0) {
		perror("socket");
		exit(1);
	}

	/*2. 绑定 */
        /*2.1 填充struct sockaddr_in结构体变量 */
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT); //网络字节序的端口号
#if 0
	sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); 
#else
	if( inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1) {
		perror("inet_pton");
		exit(1);
	}
#endif	
        /*2.2 绑定 */	
	if( bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0 ) {
		perror("bind");
		exit(1);
	}
}

如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程

3、listen()

int listen (int sockfd, int backlog);把主动套接字变成被动套接字

  • sockfd:监听连接的套接字(通过socket()函数拿到的fd)
  • backlog同时允许几路客户端和服务器进行正在连接的过程(正在三次握手)
    一般填5,测试得知,ARM最大为8
    • 指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
    • DoS(拒绝服务)攻击即利用了这个原理,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。
  • 返回值: 0 或 -1
  • 完成listen()调用后,socket变成了监听socket(listening socket)

内核中服务器的套接字fd会维护2个链表:

  • 正在三次握手的的客户端链表(数量=2*backlog+1
  • 已经建立好连接的客户端链表(已经完成3次握手分配好了newfd)
  • 比如:listen(fd, 5); //表示系统允许11(=2*5+1)个客户端同时进行三次握手

返回值:

  • RETURN VALUE
           On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
    

4、accept()

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;(阻塞等待客户端连接请求)

  • 返回值:已建立好连接的套接字或-1

  • 头文件

    • #include <sys/types.h>
      #include <sys/socket.h> 
      
  • sockfd : 监听套接字(经过前面socket()创建并通过bind(),listen()设置过的fd)

  • addr : 对方地址(获取连接过来的客户的信息)

  • addrlen:地址长度(获取连接过来的客户的信息)

listen()accept()是TCP服务器端使用的函数

返回值

  •  RETURN VALUE
           On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket. On  error, -1 is returned, and errno is set appropriately.
    
  • 成功时返回已经建立好连接的新的newfd


代码举例:

创建文件net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */

#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5

#define QUIT_STR "quit"

#endif

创建服务器文件server.c

#include "net.h"

int main (void)
{

	int fd = -1;
	struct sockaddr_in sin;

	/* 1. 创建socket fd */
	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	/*2. 绑定 */
	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号

	/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
	sin.sin_addr.s_addr = htonl (INADDY_ANY);
#else
	if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}
#endif
	/*2.2 绑定 */
	if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("bind");
		exit (1);
	}

	/*3. 调用listen()把主动套接字变成被动套接字 */
	if (listen (fd, BACKLOG) < 0) {
		perror ("listen");
		exit (1);
	}
	printf ("Server starting....OK!\n");
	int newfd = -1;
	/*4. 阻塞等待客户端连接请求 */
#if 0
	newfd = accept (fd, NULL, NULL);
	if (newfd < 0) {
		perror ("accept");
		exit (1);
	}
#else
	/*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号 */
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof (cin);
	if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
		perror ("accept");
		exit (1);
	}

	char ipv4_addr[16];
	if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
		perror ("inet_ntop");
		exit (1);
	}

	printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntons (cin.sin_port));

#endif
	/*5. 读写 */
	//..和newfd进行数据读写
	int ret = -1;
	char buf[BUFSIZ];
	while (1) {
		bzero (buf, BUFSIZ);
		do {
			ret = read (newfd, buf, BUFSIZ - 1);
		} while (ret < 0 && EINTR == errno);
		if (ret < 0) {

			perror ("read");
			exit (1);
		}
		if (!ret) {				//对方已经关闭
			break;
		}
		printf ("Receive data: %s\n", buf);

		if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符
			printf ("Client is exiting!\n");
			break;
		}
	}
	close (newfd);

	close (fd);
	return 0;
}

5、connect()

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);(客户端的连接函数)

  • 返回值:0 或 -1

  • 头文件:

    • #include <sys/types.h>
      #include <sys/socket.h> 
      
  • sockfd : socket返回的文件描述符(通过socket()函数拿到的fd)

  • serv_addr : 服务器端的地址信息(struct sockaddr的结构体变量的地址)

  • addrlen : serv_addr的长度(地址长度)

connect()是客户端使用的系统调用。

返回值:

  • RETURN VALUE
           If the connection or binding succeeds, zero is returned.  On error, -1 is returned, and errno is set appropriately.
    

代码举例:创建文件client1.c

#include "net.h"

int main (void)
{
	int fd = -1;
	struct sockaddr_in sin;
	/* 1. 创建socket fd */
	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}
	/*2.连接服务器 */

	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));

	sin.sin_family = AF_INET;
	sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号
#if 0
	sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
	if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}
#endif

	if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("connect");
		exit (1);
	}

	printf ("Client staring...OK!\n");
	/*3. 读写数据 */
	char buf[BUFSIZ];
	int ret = -1;
	while (1) {
		bzero (buf, BUFSIZ);
		if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {
			continue;
		}
		do {
			ret = write (fd, buf, strlen (buf));
		} while (ret < 0 && EINTR == errno);

		if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符
			printf ("Client is exiting!\n");
			break;
		}
	}

	/*4.关闭套接字 */
	close (fd);
}

6、send()/write()

ssize_t send(int socket, const void *buffer, size_t length, int flags);

  • 返回值:
    • 成功:实际发送的字节数
    • 失败:-1, 并设置errno
  • 头文件:
    • #include <sys/socket.h>
  • buffer : 发送缓冲区首地址
  • length : 发送的字节数
  • flags : 发送方式(通常为0)

ssize_t write(int fd, const void *buf, size_t count);

  • send()比write多一个参数flags:
    • flags:
      • 一般填写0,此时与write()作用一样
      • MSG_DONTWAIT:Enables nonblocking operation(非阻塞标志)
      • MSG_OOB:用于发送TCP类型的带外数据(out-of-band)

read()和write()经常会代替recv()和send(),通常情况下,看程序员的偏好

使用read()/write()和recv()/send()时最好统一

7、recv()/read()

ssize_t recv(int socket, const void *buffer, size_t length, int flags);

  • 返回值:
    • 成功:实际接收的字节数
    • 失败:-1, 并设置errno
  • 头文件:
    • #include <sys/socket.h>
  • buffer : 发送缓冲区首地址
  • length : 发送的字节数
  • flags : 接收方式(通常为0)

ssize_t read(int fd, voide *buf, size_t count);

flags:

  • 一般填写0,此时和read()作用相同

特殊标志:

  • MSG_DONTWAIT:Enables nonblocking operation(非阻塞标志)
  • MSG_OOB:用于发送TCP类型的带外数据(out-of-band)
  • MSG_PEEK:

8、套接字的关闭

int close(int sockfd);

  • 关闭双向通讯

int shutdown(int sockfd, int howto);

  • TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown。
  • 针对不同的howto,系统回采取不同的关闭方式。

shutdown()的howto参数

  • howto = 0
    • 关闭读通道,但是可以继续往套接字写数据。
  • howto = 1
    • 和上面相反,关闭写通道。只能从套接字读取数据。
  • howto = 2
    • 关闭读写通道,和close()一样

9、TCP循环服务器模型

socket(...);
bind(...);
listen(...);
while(1)
{
   accept(...);
   process(...);
   close(...);
}

TCP服务器一般很少用

10、TCP多线程服务器

socket(...);
bind(...);
listen(...);
while(1)
{
   accpet(...);
   if((pthread_create(...))!==-1)   {
     process(...);
     close(...);
     exit(...);   
     }   
     close(...);
}

多线程服务器是对多进程的服务器的改进

代码举例:net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */

#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5

#define QUIT_STR "quit"

#endif

server.c

#include <pthread.h>
#include "net.h"

void cli_data_handle (void *arg);

int main (void)
{

	int fd = -1;
	struct sockaddr_in sin;

	/* 1. 创建socket fd */
	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	/*优化4: 允许绑定地址快速重用 */
	int b_reuse = 1;
	setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));


	/*2. 绑定 */
	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号

	/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
	sin.sin_addr.s_addr = htonl (INADDR_ANY);
#else
	if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}
#endif
	/*2.2 绑定 */
	if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("bind");
		exit (1);
	}

	/*3. 调用listen()把主动套接字变成被动套接字 */
	if (listen (fd, BACKLOG) < 0) {
		perror ("listen");
		exit (1);
	}
	printf ("Server starting....OK!\n");
	int newfd = -1;
	/*4. 阻塞等待客户端连接请求 */

/* 优化: 用多进程/多线程处理已经建立号连接的客户端数据 */
	pthread_t tid;

	struct sockaddr_in cin;
	socklen_t addrlen = sizeof (cin);

	while (1) {
		if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
			perror ("accept");
			exit (1);
		}

		char ipv4_addr[16];
		if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
			perror ("inet_ntop");
			exit (1);
		}

		printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, htons (cin.sin_port));

		pthread_create (&tid, NULL, (void *) cli_data_handle, (void *) &newfd);
	}

	close (fd);
	return 0;
}

void cli_data_handle (void *arg)
{
	int newfd = *(int *) arg;

	printf ("handler thread: newfd =%d\n", newfd);

	//..和newfd进行数据读写
	int ret = -1;
	char buf[BUFSIZ];
	while (1) {
		bzero (buf, BUFSIZ);
		do {
			ret = read (newfd, buf, BUFSIZ - 1);
		} while (ret < 0 && EINTR == errno);
		if (ret < 0) {

			perror ("read");
			exit (1);
		}
		if (!ret) {				//对方已经关闭
			break;
		}
		printf ("Receive data: %s\n", buf);

		if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符
			printf ("Client(fd=%d) is exiting!\n", newfd);
			break;
		}
	}
	close (newfd);

}

client.c


/*./client serv_ip serv_port */
#include "net.h"

void usage (char *s)
{
	printf ("\n%s serv_ip serv_port", s);
	printf ("\n\t serv_ip: server ip address");
	printf ("\n\t serv_port: server port(>5000)\n\n");
}

int main (int argc, char **argv)
{
	int fd = -1;

	int port = -1;
	struct sockaddr_in sin;

	if (argc != 3) {
		usage (argv[0]);
		exit (1);
	}
	/* 1. 创建socket fd */
	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	port = atoi (argv[2]);
	if (port < 5000) {
		usage (argv[0]);
		exit (1);
	}
	/*2.连接服务器 */

	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));

	sin.sin_family = AF_INET;
	sin.sin_port = htons (port);	//网络字节序的端口号
#if 0
	sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
	if (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}
#endif

	if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("connect");
		exit (1);
	}

	printf ("Client staring...OK!\n");
	/*3. 读写数据 */
	char buf[BUFSIZ];
	int ret = -1;
	while (1) {
		bzero (buf, BUFSIZ);
		if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {
			continue;
		}
		do {
			ret = write (fd, buf, strlen (buf));
		} while (ret < 0 && EINTR == errno);

		if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符
			printf ("Client is exiting!\n");
			break;
		}
	}

	/*4.关闭套接字 */
	close (fd);
}

11、TCP多进程并发服务器

socket(...);
bind(...);
listen(...);
while(1){
   accpet(...);
   if(fork(...) == 0)
   {
    process(...);
    close(...);
    exit(...);
    }
    close(...);
}

TCP并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。

代码举例:创建文件net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */

#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5

#define QUIT_STR "quit"

#endif

server.c

#include <pthread.h>
#include <signal.h>
#include "net.h"

void cli_data_handle (void *arg);

void sig_child_handle(int signo)
{
	if(SIGCHLD == signo) {
		waitpid(-1, NULL,  WNOHANG);
	}
}
int main (void)
{

	int fd = -1;
	struct sockaddr_in sin;
	
	signal(SIGCHLD, sig_child_handle);	

	/* 1. 创建socket fd */
	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	/*优化4: 允许绑定地址快速重用 */
	int b_reuse = 1;
	setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));


	/*2. 绑定 */
	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号

	/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
	sin.sin_addr.s_addr = htonl (INADDR_ANY);
#else
	if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}
#endif
	/*2.2 绑定 */
	if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("bind");
		exit (1);
	}

	/*3. 调用listen()把主动套接字变成被动套接字 */
	if (listen (fd, BACKLOG) < 0) {
		perror ("listen");
		exit (1);
	}
	printf ("Server starting....OK!\n");
	int newfd = -1;
	/*4. 阻塞等待客户端连接请求 */
	
        struct sockaddr_in cin;
        socklen_t addrlen = sizeof (cin);
	while(1) {
		pid_t pid = -1;
		if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
                        perror ("accept");
                        break;
                }
		/*创建一个子进程用于处理已建立连接的客户的交互数据*/
		if((pid = fork()) < 0) {
			perror("fork");
			break;
		}
		
		if(0 == pid) {  //子进程中
			close(fd);
			char ipv4_addr[16];
                
			if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
                        	perror ("inet_ntop");
                        	exit (1);
               	 	}

               	 	printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));	
			cli_data_handle(&newfd);		
			return 0;	
		
		} else { //实际上此处 pid >0, 父进程中 
			close(newfd);
		}
		

	}		


	close (fd);
	return 0;
}

void cli_data_handle (void *arg)
{
	int newfd = *(int *) arg;

	printf ("Child handling process: newfd =%d\n", newfd);

	//..和newfd进行数据读写
	int ret = -1;
	char buf[BUFSIZ];
	while (1) {
		bzero (buf, BUFSIZ);
		do {
			ret = read (newfd, buf, BUFSIZ - 1);
		} while (ret < 0 && EINTR == errno);
		if (ret < 0) {

			perror ("read");
			exit (1);
		}
		if (!ret) {				//对方已经关闭
			break;
		}
		printf ("Receive data: %s\n", buf);

		if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符
			printf ("Client(fd=%d) is exiting!\n", newfd);
			break;
		}
	}
	close (newfd);

}

client.c


/*./client serv_ip serv_port */
#include "net.h"

void usage (char *s)
{
	printf ("\n%s serv_ip serv_port", s);
	printf ("\n\t serv_ip: server ip address");
	printf ("\n\t serv_port: server port(>5000)\n\n");
}

int main (int argc, char **argv)
{
	int fd = -1;

	int port = -1;
	struct sockaddr_in sin;

	if (argc != 3) {
		usage (argv[0]);
		exit (1);
	}
	/* 1. 创建socket fd */
	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	port = atoi (argv[2]);
	if (port < 5000) {
		usage (argv[0]);
		exit (1);
	}
	/*2.连接服务器 */

	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));

	sin.sin_family = AF_INET;
	sin.sin_port = htons (port);	//网络字节序的端口号
#if 0
	sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
	if (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}
#endif

	if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("connect");
		exit (1);
	}

	printf ("Client staring...OK!\n");
	/*3. 读写数据 */
	char buf[BUFSIZ];
	int ret = -1;
	while (1) {
		bzero (buf, BUFSIZ);
		if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {
			continue;
		}
		do {
			ret = write (fd, buf, strlen (buf));
		} while (ret < 0 && EINTR == errno);

		if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符
			printf ("Client is exiting!\n");
			break;
		}
	}

	/*4.关闭套接字 */
	close (fd);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值